Differences between SQLAlchemy Relationships - python

Being new to SQLAlchemy and SQL, the relationships in SQLAlchemy is confusing to me.
Is this set of SQLAlchemy model definitions (based on One-To-Many in the official docs)
class Invoice(Base):
__tablename__ = 'invoices'
id = Column(Integer, primary_key=True)
customer = relationship("Customer")
class Customer(Base):
__tablename__ = 'customers'
id = Column(Integer, primary_key=True)
invoice_id = Column(Integer, ForeignKey('invoices.id'))
identical to
class Invoice(Base):
__tablename__ = 'invoices'
id = Column(Integer, primary_key=True)
class Customer(Base):
__tablename__ = 'customers'
id = Column(Integer, primary_key=True)
invoice_id = Column(Integer, ForeignKey('invoices.id'))
invoice = relationship("Invoice")
I have been defining my models using the second approach and it appears to work fine so far. However I am unsure whether this is the correct way to go about setting up relationships.
What is the difference between the two?

Each of those models is the same as far as the resulting database tables and columns go. The difference is in how you are using the relationship() directive that enables the ORM to interact with the foreign key relationship between those tables. I should note that you do not need to create these relationship attributes at all if you don't need / want the extra help from the ORM in interacting with these relationships between tables.
In your first example, you are creating a customer attribute for Invoice that would enable you to do things like access any and all customers associated with a particular invoice. For example, you could print each customer id associated with a particular invoice.
invoice = session.query(Invoice).filter(Invoice.id == 1).first()
for c in invoice.customer:
print(c.id)
In your second example, you are creating an invoice attribute for Customer that would enable you to do things like access the invoice data associated with a particular customer. For example, you could print the customer's invoice id (this would be more useful if you had other Invoice columns not already referenced by foreign key in Customer).
customer = session.query(Customer).filter(Customer.id == 1).first()
print(customer.invoice.id)
In the event that you would like to access these attributes on both sides of the relationship so that you could use the ORM in both of the ways described above (among others), you could use either the back_populates or backref parameters to connect the two relationships. You can learn more about these options at Linking Relationships with Backref.
class Invoice(Base):
__tablename__ = 'invoices'
id = Column(Integer, primary_key=True)
customers = relationship("Customer", back_populates="invoice")
class Customer(Base):
__tablename__ = 'customers'
id = Column(Integer, primary_key=True)
invoice_id = Column(Integer, ForeignKey('invoices.id'))
invoice = relationship("Invoice", back_populates="customers")

Related

SQLAlchemy model inheritance without relations

I want to create two similar tables. I need not to create any realtions between them.
class Table1(Base):
__tablename__ = "table1"
id = Column(UUIDType, primary_key=True, index=True, unique=True)
value = Column(String(70), nullable=True)
class Table2(Table1):
__tablename__ = "table2"
unit = Column(String(10), nullable=True)
But when I try to use that models I get
E sqlalchemy.exc.NoForeignKeysError: Can't determine the inherit condition between inherited table 'table1' and inheriting table 'table2';
Is it possible to refuse automatic creation of the relations without abstract class usage?

What happens if I specify OneToMany relationship only from one side in flask-sqlalchemy?

I have a OneToMany relationship between 2 entities in flask. I also specified the relationship only on one side. I am unsure what the difference is between the following:
class CustomJob(db.Model):
__tablename__ = "custom_job"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
country_from = db.Column(db.Integer, db.ForeignKey('country.id'))
class Country(db.Model):
__tablename__ = "country"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
custom_jobs = db.relationship('CustomJob', backref="country", lazy=False)
Or just specify the foreign key on master entity:
class CustomJob(db.Model):
__tablename__ = "custom_job"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
country_from = db.Column(db.Integer, db.ForeignKey('country.id'))
will is there performance difference between the two ?
The brilliance behind an ORM like SQLAlchemy is that it can detect relationships between models based on foreign key constraints. So once you've declared your foreign key on the custom_job table, the relationship is configured in the database.
Mapping that relationship to your python objects is another useful part of ORM's. Here, you are doing that with db.relationship. By specifying backref, you are essentially telling the ORM to make the relationship available on the other object.
Let me explain more explicitly using the code provided in your Q:
class Country(db.Model):
__tablename__ = 'country'
...
custom_jobs = db.relationship('CustomJob', backref='custom_job', lazy=False)
...
The Country model you've defined will map all associated rows from the custom_job table through the attribute Country.custom_jobs.
This relationship will propagate to the CustomJob model and allow you to access the associated rows from the country table through an attribute created by the backref parameter --> here CustomJob.custom_job.
I assume this is an error and that you intended to use backref="country"
In this case, access associated objects instead with CustomJob.country

SQLalchemy built relationship between classes

I am running into a conceptual problem I do not know how to approach, which might be due my lack of knowledge with SQLalchemy. I have two classes: People and Person and I want them each to have a column to share their respective id's with each other using the relationship function.
Now, I have an endpoint in views.py which instantiates those two classes and establishes a Child / Parent relationship. Looking at the database results however, only People, the parent class has the id stored in its respective table, while the Person table in column people is None.
I know the id in person is only generated after the commit() statement and thus None for Person, and was wondering if there is a way to solve this elegantly, or do I need to first query the current people instance, retreive its id, set the id in the person table and then commit() again?
I hope my question makes sense,thank you.
'''
model.py
'''
class People(Model):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
person = relationship('Person', back_populates='people')
person_id = Column(Integer, ForeignKey('people.id'))
class Person(Model):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
people = relationship('People', uselist=False, back_populates='person')
'''
views.py
'''
#main.route('/', methods=['GET', 'POST'])
def index():
people = People()
person = Person(people_id = ?)
people.person = person
session.add(person)
session.add(people)
session.commit()
I regret that I have not yet understood your question. However, since your code contains some errors, I will first write you my corrected variant.
class People(Model):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
person = relationship('Person', back_populates='people')
person_id = Column(Integer, ForeignKey('person.id'))
class Person(Model):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
people = relationship('People', back_populates='person')
def index():
person = Person()
people = People()
people.person = person
session.add(person)
session.add(people)
session.commit()
The question of gittert seems justified to me. It makes no sense to save the ForeignKey in both tables on the referenced identifiers of the other model.
What do you want to achieve?
If you're looking for an actual column in your database for your 'relationships', you won't find them. Your .people and .person are virtual relationships created in Python without any interaction with the SQL database.

One-to-many relationship to multiple models

I have a model Thing and a model Action. There is a one-to-many relationship between Things and Actions. However, I would like to be able to subclass Action to have (for example) BuildAction, HealAction and BloodyStupidAction. Is it possible using Flask-SQLAlchemy to do this and maintain the single one-to-many relationship?
This problem is described in the SQLAlchemy docs under Inheritance Configuration. If your different subclasses will share the same database table, you should use single table inheritance.
Code example:
class Thing(db.Model):
__tablename__ = 'thing'
id = db.Column(db.Integer, primary_key=True)
actions = db.relationship('Action', backref=db.backref('thing'))
class Action(db.Model):
__tablename__ = 'action'
id = db.Column(db.Integer, primary_key=True)
thing_id = db.Column(db.Integer, db.ForeignKey('thing.id'))
discriminator = db.Column('type', db.String(50))
__mapper_args__ = {'polymorphic_on': discriminator}
class BuildAction(Action):
__mapper_args__ = {'polymorphic_identity': 'build_action'}
time_required = db.Column(db.Integer)
Each subclass of Action should inherit the thing relationship defined in the parent class. The action.type column describes which subclass action each row of the table represents.

What's the proper way to describe an associative object by SQLalchemy the declarative way

I'm looking for a way to describe an associative object the declarative way. Beyond storing the foreign keys in the association table, I need to store information like the creation date of the association.
Today, my model looks like that :
# Define the User class
class User(Base):
__tablename__ = 'users'
# Define User fields
id = schema.Column(types.Integer(unsigned=True),
schema.Sequence('users_seq_id', optional=True), primary_key=True)
password = schema.Column(types.Unicode(64), nullable=False)
# Define the UserSubset class
class UserSubset(Base):
__tablename__ = 'subsets'
# Define UserSubset fields
id = schema.Column(types.Integer(unsigned=True),
schema.Sequence('subsets_seq_id', optional=True), primary_key=True)
some_short_description = schema.Column(types.Unicode(50), nullable=False)
# Define the subset memberships table
subset_memberships = schema.Table('group_memberships', Base.metadata,
schema.Column('user_id', types.Integer(unsigned=True), ForeignKey('users.id')),
schema.Column('subset_id', types.Integer(unsigned=True), ForeignKey('subsets.id')),
schema.Column('created', types.DateTime(), default=now, nullable=False),
)
Can I connect everything in an associative object ? Or should I change stop using the declarative way ?
What you are using at the moment is just a Many-to-Many-relation. How to work with association objects is described in the docs.
There is also an extension called associationproxy which simplifies the relation.
As you can see in the manual, configuring a one to many relation is really simple:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50))
addresses = relation("Address", backref="user")
class Address(Base):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
email = Column(String(50))
user_id = Column(Integer, ForeignKey('users.id'))
Many to many relations isn't much harder:
There’s nothing special about many-to-many with declarative. The secondary argument to relation() still requires a Table object, not a declarative class. The Table should share the same MetaData object used by the declarative base:
keywords = Table('keywords', Base.metadata,
Column('author_id', Integer, ForeignKey('authors.id')),
Column('keyword_id', Integer, ForeignKey('keywords.id'))
)
class Author(Base):
__tablename__ = 'authors'
id = Column(Integer, primary_key=True)
keywords = relation("Keyword", secondary=keywords)
You should generally not map a class and also specify its table in a many-to-many relation, since the ORM may issue duplicate INSERT and DELETE statements.
Anyway, what you seem to be doing might be better served with inheritance. Of course, there can be complex table relations that will be a pathological case for the declarative way, but this doesn't seem to be one of them.
One more thing, code comments should state what the following code does ans why, not how it does it. Having a # Define the User class comment is almost like having a line of code saying a = 1 # assing value 1 to variable "a".

Categories

Resources