I would like to have a one to many relation between two ORM objects and extend this with a second relation that links to the same 'many' object while applying a constraint.
The following example may elaborate:
class Users(SQLABase):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
addresses = relationship('Addresses', backref='user')
class Addresses(SQLABase):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
historic = (String(1))
add1 = Column(String)
user = Column(Integer, ForeignKey('users.id'))
I would like an attribute 'Users.valid_addresses' that relates to the same 'addresses' table filtering where Addresses.historic == 'N' like the following query:
Session.Query(Addresses).filter_by(historic = 'N').all()
I'm looking for the "SQLAlchemy way".
Can I apply a condition to a relation?
Am I expected to iterate over the results of the current relation?
Should I create an additional 'valid_addresses' object based on an SQL
view of the addresses table applying the condition?
I have the feeling this has already been answered but I'm failing to phrase the question correctly.
This is covered in the SQLAlchemy docs under "Specifying Alternate Join Conditions".
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
boston_addresses = relationship("Address",
primaryjoin="and_(User.id==Address.user_id, "
"Address.city=='Boston')")
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('user.id'))
street = Column(String)
city = Column(String)
state = Column(String)
zip = Column(String)
Related
I have 3 models
class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, index=True, nullable=False)
content = Column(String, nullable=False)
published = Column(Boolean)
owner_id = Column(Integer, ForeignKey("users.id"))
owner = relationship("User")
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String)
password = Column(String)
class Vote(Base):
__tablename__ = "votes"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
post_id = Column(Integer, ForeignKey("posts.id"), primary_key=True)
I would like to generate a query that retrieves one or all posts and automatically sum up all of the votes for said post. I know how to do this in sql but not sure how to accomplish this using SQLAlchemy.
The sql query would look like this:
SELECT posts.*, count(votes.post_id) as votes from posts left join votes on (posts.id = votes.post_id) where posts.id = 1 group by posts.id;
Also is there a way to make it so i don't have to create a custom query and instead have the post model automatically populate the sum of all votes with a reference or something like that?
How can i do a foreign key refer to id to multiple tables?
class HistoryActivation(Base):
__tablename__ = 'active_control'
id = Column(Integer, primary_key=True)
id_entity = Column(Integer) #This one refer to any entity (id) that has a relation with this class.
date = Column(DATE)
user = Column(String(120))
active = Column(BOOLEAN)
class Person(Base):
__tablename__ = 'persons'
id = Column(Integer, primary_key=True)
name = Column(String(50))
history_activation = relationship("HistoryActivation",
primaryjoin="HistoryActivation.id_entidade == Person.id")
class Company(Base):
__tablename__ = 'companies'
id = Column(Integer, primary_key=True)
name = Column(String(50))
history_activation = relationship("HistoryActivation",
primaryjoin="HistoryActivation.id_entidade == Company.id")
class Whatever(Base): ...
So, id_entity refer to id to various tables, something like:
id_entity = Column(Integer, ForeignKey('Person.id or Company.id or Whatever.id').
Is possible to tell ForeignKey can refer more than one relation?
Using SQLalchemy and Flask-SQLAlchemy, I'm able to set up the following User class and sub classes Customer and Provider :
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=False)
user_type = Column(String(32), nullable=False)
__mapper_args__ = {
'polymorphic_on':'user_type',
'polymorphic_identity':'user_type'
}
class Customer(User):
__tablename__ = "customer"
id = Column(Integer, ForeignKey('user.id'), primary_key=True)
note_customer = Column(Integer)
__mapper_args__ = {
'polymorphic_identity':'customer'
}
class Provider(User):
__tablename__ = "provider"
id = Column(Integer, ForeignKey('user.id'), primary_key=True)
note_provider = Column(Integer)
__mapper_args__ = {
'polymorphic_identity':'provider'
}
class Address(Base):
__tablename__ = "address"
id = Column(Integer, primary_key=True)
number = Column(Integer)
street = Column(String(100))
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship(User)
Then I am able to create a new customer :
u = Customer(name= 'Mark', user_type='customer', note_customer = 15)
db.session.add(u)
db.session.commit()
But I would like to add an address value to my Customer Mark during his creation and I don't succeed in and can't figure out how doing it.
I guess I'm wrong with my relationship : for me it's a "one-to-one" type, but many solutions I tried give me errors telling that the address argument is not available for a Customer object type.
Thanks!
The problem is that you neither use a backref or explicitly create 2 back populating relationships. Because of this the User model class has no idea of the relationship and no Address instance can be passed to it or subclasses when constructing. The error you're receiving should be:
TypeError: 'address' is an invalid keyword argument for Customer
Using backref argument of relationship
from sqlalchemy.orm import backref
class Address(Base):
__tablename__ = "address"
id = Column(Integer, primary_key=True)
number = Column(Integer)
street = Column(String(100))
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship(User, backref=backref('address', uselist=False))
This creates a relationship attribute address on User model as if you'd added it manually:
address = relationship('Address', uselist=False, ...)
You need to use a backref() object in order to pass uselist=False since you want a one to one relationship.
Using explicit relationship attributes
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=False)
user_type = Column(String(32), nullable=False)
address = relationship('Address', uselist=False, back_populates='user')
class Address(Base):
__tablename__ = "address"
id = Column(Integer, primary_key=True)
number = Column(Integer)
street = Column(String(100))
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship(User, back_populates='address')
The back_populates parameters will establish event listeners that will mirror attribute operations between the classes.
With a proper relationship configuration in place creating a new Customer with an address can be done like this:
u = Customer(name='Mark', user_type='customer', note_customer=15,
address=Address(number=221, street='Baker Street'))
session.add(u)
session.commit()
or in separate steps:
u = Customer(name='Mark', user_type='customer', note_customer=15)
u.address = Address(number=221, street='Baker Street')
session.add(u)
session.commit()
The error is because your customer class isn't defined to have an address.
You could simply add another few lines to put Mark's address in, and tie the relationship to Mark.
mark_address = Address(number='123', street='Main St', user = u)
db.session.add(mark_address)
db.session.commit()
I have an existing set of sqlalchemy models like so
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String)
first_name = Column(String)
last_name = Column(String)
email = Column(String)
user_group = Table('user_group', Base.metadata,
Column('user_id', Integer, ForeignKey('users.id')),
Column('group_id', Integer, ForeignKey('groups.id'))
)
class Group(Base):
__tablename__ = 'groups'
id = Column(Integer, primary_key=True)
name = Column(String)
members = relationship("User", secondary=user_group)
and I would like to create a third model which can have a 1 to many relationship with either a User or a Group type. So I want to end up with something that can be used like this
class Contact(Base):
__tablename__ = 'contacts'
id = Column(Integer, primary_key=True)
type_id = Column(Integer) # the id of the group or user row which it refers to.
type = Column(String) # 'group' or 'user'
class Thing(Base):
__tablename__ = 'things'
relationship('Contact') # Array of Contacts
where
thing = Thing()
thing.contacts[0].type # returns 'group' or 'user'
thing.contacts[0].contact # returns the actual User or Group object
I know this can be done with inheritance but is there any alternative? I'd rather not have to subclass my basic User and Group models.
Thanks in advance!
Read Generic Associations section of the documentation.
Your description matches the case of the Generic ForeignKey
I am using Python 2.7.2 and SQLAlchemy 0.9.3.
A Department has many groups. A Group has many members. A Member can only be in one group.
Currently to get a list of all the members of a Department. I first get all the groups and then get the members of those groups.
Is there a way to go directly from a department to members without going via groups table ?
class Department(Base):
__tablename__ = 'departments'
id = Column(Integer, primary_key=True)
name = Column(String(64), unique=True)
location = Column(String(32), unique=True)
groups = relationship("Group", backref="department")
class Group(Base):
__tablename__ = 'groups'
id = Column(Integer, primary_key=True)
name = Column(String(64), unique=True)
department_id = Column(Integer, ForeignKey('departments.id'))
members = relationship("Member", backref="group")
class Member(Base):
__tablename__ = 'members'
id = Column(Integer, primary_key=True)
group_id = Column(Integer, ForeignKey('groups.id'))
This kind of query requires a join statement:
q = session.query(Member).\
join(Member.group).\
join(Group.department).\
filter(Department.name=='depart_name')
result = q.all()
This will return a list of Member Objects where the member is part of the department depart_name