i have users and entities (many-to-many) , and using sqlalchemy, with this model:
from sqlalchemy import Table, Column, Unicode, Integer, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
from zope.sqlalchemy import ZopeTransactionExtension
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()
users_entities = Table('users_entities', Base.metadata,
Column('userID', Integer, ForeignKey('users.id')),
Column('entitieID', Integer, ForeignKey('entities.id'))
)
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(Unicode(20))
password = Column(Unicode(101))
entities = relationship("Entities", secondary=users_entities)
def __init__(self, username, password):
self.username = username
self.password = password
class Entities(Base):
__tablename__ = 'entities'
id = Column(Integer, primary_key=True)
name = Column(Unicode(20))
descr = Column(Unicode(101))
url = Column(Unicode(101))
def __init__(self, name, descr, url):
self.name = name
self.descr = descr
self.url = url
so, when i use:
user = dbsession.query(User).filter_by(id=session["userID"]).first()
entities = user.entities
i get the user with the user data, and the entities(user.entities) with all the user's entities.
but instead of getting all the entities, now i need to get an entity from the user where the id is = X
something that would work like this:
user.query(Entities).filter_by(id=X)
i can't find a simple("best") way to do this, am i missing something?
You are looking for Dynamic Relationship Loaders, they're exactly what you want to do, and enable you to write:
user.entities.filter_by(id=X)
By the way, you can write user = dbsession.query(User).get(session["userID"]). It does the same query, but only if the object is not already in the session cache (plus, it's shorter).
Related
I am new in Sqlalchemy and trying to achieve the following goal with relationship():
There is an User table which stores user data.
Every user is able to invite other user with an invite_code.
Every user keeps a list of invitation, every invitation includes the invite_code and the invitee User
I think the relationship between User and Invitation is one-to-many. Since Invitation contains User, then I think it is probably better to use self-referential relationship to represent the inviter-to-invitaions(invitees) relationship and use an association object to store the invite_code.
I checked the sqlalchemy documentation and the question, tried to implement the classed like this:
from sqlalchemy import Column, Integer, ForeignKey, create_engine, String
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Invitation(Base):
__tablename__ = 'invitation'
invite_code = Column(Integer)
inviter_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
invitee_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
invitee = relationship('User') #Need HELP here
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
inviters = relationship('User',
secondary='invitation',
primaryjoin=id==Invitation.invitee_id,
secondaryjoin=id==Invitation.inviter_id,
backref='invitees')
invitations = relationship('Invitation')# Need HELP here
def __repr__(self):
return f'User: {self.name}'
if __name__ == '__main__':
engine = create_engine('sqlite://')
Base.metadata.create_all(engine)
Session = sessionmaker(engine)
db = Session()
inviter1 = User(name='inviter1')
inviter2 = User(name='inviter2')
invitee1= User(name='invitee1')
invitee2 = User(name='invitee2')
inviter1.invitees = [invitee1, invitee2]
inviter2.invitees = [invitee1]
db.add(inviter1)
db.add(inviter2)
db.add(invitee1)
db.add(invitee2)
db.commit()
users = db.query(User).all()
for user in users:
print(user)
print(' Inviter: ', user.inviters)
print(' Invitee: ', user.invitees)
print()
If the lines with comment #Need HELP here are deleted, I can get the corresponding inviters and invitees, but cannot get the invite_code. If the #Need HELP here code are added, the error is:
Exception has occurred: AmbiguousForeignKeysError
Could not determine join condition between parent/child tables on relationship Invitation.invitee - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
Is there a way to add extra data column in association object like association object for many-to-many relationship for self referential table?
Sorry for the too much text, I didn't find any reference document on the web.
Finally, I figured it out with the help of foreign_keys:
from sqlalchemy import Column, Integer, ForeignKey, create_engine, String
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
sent_invitations = relationship('Invitation', foreign_keys='Invitation.inviter_id', back_populates='inviter', cascade='all, delete')
received_invitations=relationship('Invitation', foreign_keys='Invitation.invitee_id', back_populates='invitee', cascade='all, delete')
def __repr__(self):
return f'User: {self.name}'
class Invitation(Base):
__tablename__ = 'invitation'
id = Column(Integer, primary_key=True)
invite_code = Column(Integer)
inviter_id = Column(Integer, ForeignKey('user.id'))
invitee_id = Column(Integer, ForeignKey('user.id'))
inviter=relationship('User', foreign_keys=[inviter_id], back_populates='sent_invitations')
invitee=relationship('User', foreign_keys=[invitee_id], back_populates='received_invitations')
def __repr__(self):
return f'Invitation: {self.inviter} invited {self.invitee} with {self.invite_code}'
if __name__ == '__main__':
engine = create_engine('sqlite://')
Base.metadata.create_all(engine)
Session = sessionmaker(engine)
db = Session()
inviter1 = User(name='inviter1')
inviter2 = User(name='inviter2')
invitee1= User(name='invitee1')
invitee2 = User(name='invitee2')
invitation1 = Invitation(invite_code=50, inviter=inviter1, invitee=invitee1)
invitation2 = Invitation(invite_code=20, inviter=inviter2, invitee=invitee2)
invitation3 = Invitation(invite_code=22, inviter=inviter1, invitee=inviter2)
invitation4 = Invitation(invite_code=44, inviter=invitee1, invitee=inviter2)
db.add(inviter1)
db.add(inviter2)
db.add(invitee1)
db.add(invitee2)
db.commit()
users = db.query(User).all()
for user in users:
print(user)
print(' sent_invitation: ', user.sent_invitations)
print(' received_invitation: ', user.received_invitations)
print()
invitations = db.query(Invitation).all()
for invitation in invitations:
print(invitation)
db.delete(inviter1)
db.delete(invitee2)
db.commit()
lets say I've 2 tables users and devices. They have relation one-to-many.
In Sql, I can solve the mentioned problem by following query.
SELECT
users.*, devices.*
FROM
users
LEFT JOIN ( SELECT d1.*
FROM devices as d1
LEFT JOIN devices AS d2
ON d1.user_id = d2.user_id AND d1.date < d2.date
WHERE d2.user_id IS NULL ) as device_temp
ON (users.id = device_temp.user_id)
Here is my python code
#user_model.py
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
first_name = Column(String(500), nullable=False)
last_name = Column(String(250), nullable=False)
device_model.py
#device_model.py
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, relation
from sqlalchemy import create_engine
from user_model import User
Base = declarative_base()
class DeviceModel(Base):
__tablename__ = 'device'
id = Column(Integer, primary_key=True)
created_at = Column(DateTime(), nullable=False)
device_id = Column(String(250), nullable=False)
user_uid = Column(String, ForeignKey((User.id)))
owner = relation(User, backref='user_device')
run.py
#run.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from user_model import User, Base
from sleep_session import SleepSession, Base
from device_model import DeviceModel, Base
engine = create_engine(connection_string)
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
query = session.query(User,DeviceModel).join(DeviceModel)
results = query.all()
for row in results:
print(row.User.first_name +" "+ row.DeviceModel.device_id + " "+ str(row.DeviceModel.created_at))
I know this type of question is asked multiple times, but I could not find one with SqlAlchemy ORM.
I want the same result as described here
Thanks.
I used this question to practive sqlalchemy as I'm new to it.
Closest answer I can get is the following:
If you want to see 1 file full workable code go into the edits - I'll remove boilerplate code
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
from sqlalchemy.orm import relationship, relation
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
connection_string = 'postgres://postgres:password#localhost/test'
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
first_name = Column(String(500), nullable=False)
last_name = Column(String(250), nullable=False)
class DeviceModel(Base):
__tablename__ = 'device'
id = Column(Integer, primary_key=True)
created_at = Column(DateTime(), nullable=False)
device_id = Column(String(250), nullable=False)
user_uid = Column(Integer, ForeignKey((User.id))) # error Key columns "user_uid" and "id" are of incompatible types: character varying and integer.
owner = relation(User, backref='user_device')
engine = create_engine(connection_string)
Base.metadata.bind = engine
#User.__table__.create(engine)
#DeviceModel.__table__.create(engine)
DBSession = sessionmaker(bind=engine)
session = DBSession()
My Answer:
from sqlalchemy import and_, or_
from sqlalchemy.orm import aliased
DeviceModel2 = aliased(DeviceModel)
subquery = (session
.query(DeviceModel.created_at)
.outerjoin(DeviceModel2,
and_(DeviceModel.user_uid == DeviceModel2.user_uid,
DeviceModel.created_at < DeviceModel2.created_at))
.filter(DeviceModel2.user_uid == None)
.subquery('subq'))
query = (session
.query(User, DeviceModel)
.outerjoin(DeviceModel)
.filter(or_(
DeviceModel.created_at == subquery.c.created_at,
DeviceModel.id == None)))
print(query)
results = query.all()
for row in results:
if row[1]:
print({**row.User.__dict__, **row.DeviceModel.__dict__})
else:
print(row.User.__dict__)
from db_config import connection_string
from sqlalchemy import create_engine , and_ , inspect
from sqlalchemy.orm import sessionmaker, aliased
from user_model import User, Base
from device_model import DeviceModel, Base
engine = create_engine(connection_string)
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
DeviceModel_aliased = aliased(DeviceModel)
#make sub-query
query_for_latest_device = session.query(DeviceModel).\
outerjoin(DeviceModel_aliased,
and_(DeviceModel_aliased.user_uid == DeviceModel.user_uid,
DeviceModel_aliased.created_at > DeviceModel.created_at)).\
filter(DeviceModel_aliased.id == None).\
subquery()
use_subquery_and_join = session.query(User.first_name,latest_device).\
join(query_for_latest_device,
query_for_latest_device.c.user_uid == User.user_id).\
all()
for row in join_user_and_device:
print(row._asdict())
I'm using Python Sqlalchemy for MYSQL db. I wrote the following script to create the class object and then added some rows in the table.
from sqlalchemy import create_engine, MetaData, Table, Column, ForeignKey
from sqlalchemy.dialects.mysql.base import VARCHAR, LONGTEXT, INTEGER
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine("mysql+mysqldb://root:#localhost/mydb")
connection = engine.connect()
Session = sessionmaker(bind=engine)
session = Session()
Base = declarative_base()
metadata = MetaData()
class User(Base):
__tablename__ = 'User'
id = Column('id', INTEGER(display_width=11), primary_key=True, nullable=False)
email = Column('email', VARCHAR(charset='utf8mb4', collation='utf8mb4_0900_ai_ci', length=100), unique=True)
password = Column('password', VARCHAR(charset='utf8mb4', collation='utf8mb4_0900_ai_ci', length=45))
name = Column('name', VARCHAR(charset='utf8mb4', collation='utf8mb4_0900_ai_ci', length=100))
Now, I need to get all the rows from the table "User" so I am doing this:
user = session.query(User).all()
print(user)
but the output I am getting is not the table data but this:
[<__main__.User object at 0x7f10b0c6ebe0>, <__main__.User object at 0x7f10b0c6ec50>]
How would I get the actual data from the table? Any help would be appreciated
The output you will get is a tuple of records.
So, use a loop
users = session.query(User).all()
for user in users:
print (user)
print (user.id, user.email, user.password, user.name)
you should write __str__ method in User class something like:
class User(Base):
...
def __str__(self):
str_out = 'id={} email={} password={} name={}'
str_formated = str_out.format(self.id,self.email,self.password,self.name)
return str_formated
I have a question about sqlalchemy and DB normalization.
I have a table called Accounts, and 2 kinds of persons, Natural_Persons and Legal_Persons.
I need to relate an account to just one person at a time.
For example, the account ID 4 is related with the Natural_Person ID 5.
But... How can I know when I query that information, if the ID 5 in the account record is from a Natural Person or a Legal one?
The simplest solution (for me at the moment) is to add a new field to the Accounts table called person_type, and to use, for example, a char to differentiate them.
So now I have a record in the accounts table with the following data:
account_id = 4
person_id = 5
person_type = N
But now I want to use the DB with sqlalchemy.
If I load an account record using a Account class instance then if I access the "person" attribute it should check the person_type field and create an instance of NaturalPerson class, or LegalPerson class according to the case!
Something like:
acc = Account(4)
acc.person
"""
if person_type == "L", person returns a LegalPerson instance
but otherwise ...
"""
Table inheritance is what you are looking for:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy import create_engine, Column, Integer, ForeignKey, String
Base = declarative_base()
class Account(Base):
__tablename__ = 'account'
id = Column(Integer, primary_key=True)
person_id = Column(Integer, ForeignKey('person.id'))
person = relationship("Person")
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
name = Column(String(50))
type = Column(String(20))
__mapper_args__ = {
'polymorphic_on':type,
'polymorphic_identity':'base'
}
class NaturalPerson(Person):
__mapper_args__ = {
'polymorphic_identity':'natural'
}
class LegalPerson(Person):
__mapper_args__ = {
'polymorphic_identity':'legal'
}
engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
a = Account()
np = NaturalPerson()
a.person = np
session.add(a)
a = session.query(Account).first()
print type(a.person)
Hello I am new with SQLalchemy and have some problems with inserting data in a column.
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey, Boolean
from sqlalchemy.orm import relationship, backref
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///test.db')
Base = declarative_base()
class Users(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String, unique=True)
password = Column(String)
email = Column(String)
def __init__(self, name, password, email):
self._name = name
self._password = password
self._email = email
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
Session.configure(bind=engine)
session = Session()
dm_user = Users("Dungeonmaster", "123", "email")
print dm_user.name
print dm_user.password
print dm_user.email
session.add(dm_user)
our_user = session.query(Users).filter_by(name='Dungeonmaster').first()
session.commit()
I used sqlite studio to see if the data is added, and I see that a new row is being made (with a new id.) but the data name, password and email is not inserted.
With the print I see even before I try to add something goes wrong, but I don't know what. please help me out a bit
( dm_user = Users("Dungeonmaster", "123", "email") tried with single quotes as well (helped me before using sqlalchemy, but no difference here)
Your init method does not initialize the persistent columns. Try instead (remove underscores):
def __init__(self, name, password, email):
self.name = name
self.password = password
self.email = email