UPDATE:
It just works.
Even though you can't see the pg_* tables in reflection, you can build models on them.
So the answer is the question!
There's just too much going on here to connect the dots.
I have a Postgres database (schema? catalog?) with a set of tables. I have created models using Base = declarative_base().
E.g:
class MyModel(Base):
__tablename__ = 'mymodel'
id = Column(UUIDType(binary=False), primary_key=True, default=uuid.uuid4)
timezone = Column(Text)
first_name = Column(Text)
There's this awesome table in pg_catalog: pg_timezone_names which I would like to join to the timezone column here.
pg_timezone_names is a view (not that is relevant necessarily) and in SQLA would look something like this (though I believe I SHOULD be able to build this automatically with reflect):
class TimezoneNames(Base):
__tablename__ = 'pg_timezone_names'
name = Column(Text, primary_key=True)
abbrev = Column(Text)
utc_offset = Column(Interval)
is_dst = Column(Boolean)
Let's say I connect with:
engine = create_engine("postgres://localhost:5432/mydb")
I would like to be able to grab a record from MyModel and access the utc_offset by creating a relationship with the pg_timezone_names table.
When I try to grab Base.classes, I only see mymodel but not any of the pg_catalog tables. Yet when I'm in mydb I can perform:
SELECT * from pg_timezone_names WHERE name LIKE "%America%";
In other words, it's totally easy in psql to perform the query. But how do I specify the model connects to that table?
I believe I could change my model to:
class MyModel(Base):
__tablename__ = 'mymodel'
id = Column(UUIDType(binary=False), primary_key=True, default=uuid.uuid4)
timezone_name = Column(Text ForeignKey('pg_timezone_names.name'))
first_name = Column(Text)
timezone = relationship("TimezoneNames")
So that I can grab the record's utc_offset with something like this:
myOffset = session.query(MyModel).first().timezone.utc_offset
The question is:
How do I tell sqlalchemy to find pg_timezone_names so I can create the relationships I want?
Thanks for your consideration!
Related
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.
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")
I am using Flask-SQLAlchemy to define my models, and then using Flask-Migrate to auto-generate migration scripts for deployment onto a PostgreSQL database. I have defined a number of SQL Views on the database that I use in my application like below.
However, Flask-Migrate now generates a migration file for the view as it thinks it's a table. How do I correctly get Flask-Migrate / Alembic to ignore the view during autogenerate?
SQL View name: vw_SampleView with two columns: id and rowcount.
class ViewSampleView(db.Model):
__tablename__ = 'vw_report_high_level_count'
info = dict(is_view=True)
id = db.Column(db.String(), primary_key=True)
rowcount = db.Column(db.Integer(), nullable=False)
Which means I can now do queries like so:
ViewSampleView.query.all()
I tried following instructions on http://alembic.zzzcomputing.com/en/latest/cookbook.html and added the info = dict(is_view=True) portion to my model and the following bits to my env.py file, but don't know where to go from here.
def include_object(object, name, type_, reflected, compare_to):
"""
Exclude views from Alembic's consideration.
"""
return not object.info.get('is_view', False)
...
context.configure(url=url,include_object = include_object)
I think (though haven't tested) that you can mark your Table as a view with the __table_args__ attribute:
class ViewSampleView(db.Model):
__tablename__ = 'vw_report_high_level_count'
__table_args__ = {'info': dict(is_view=True)}
id = db.Column(db.String(), primary_key=True)
rowcount = db.Column(db.Integer(), nullable=False)
I am having some difficulty setting up a one to one relationship between two models in my flask application. I have two models Employeeand `Photo'. An employee has only one photo associated with it and vice-versa.
This is the code that I have in my models.py file:
class Employee(db.Model):
__tablename__ = 'employees'
id = db.Column(db.Integer, primary_key=True)
photo = db.relationship("Photo", uselist=False, back_populates='employees')
class Photo(db.Model):
__tablename__ = 'photos'
id = db.Column(db.Integer, primary_key=True)
employee_id = db.Column(db.Integer, db.ForeignKey('employees.id'))
employee = db.relationship('Photo', back_populates='photo')
I've followed the instruction on the SQL Alchemy documentation found hereSQL Alchemy simple relationships. The error that I keep encountering is shown below:
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship Photo.employee
- there are no foreign keys linking these tables.
Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.
I clearly specify the foreign key right here employee_id = db.Column(db.Integer, db.ForeignKey('employees.id')) . I'm not sure what I'm doing wrong. Additionally, I was reading the documentation and it doesn't help that uselist, backref, and back_populates are so similar.
Can someone assist me with this? Help would be greatly appreciated.
One to One relationship stack overflow question
backref automatically adds the reverse relationship to the related model. You can pass a db.backref object to it to specify options to the relationship. back_populates tells SQLAlchemy to populate an existing reverse relationship, rather than creating it. uselist tells SQLAlchemy whether the relationship is a list or not, for cases where it can't determine that automatically.
In your example, you need one relationship, with one backref that is a single item.
You have two typos in your code. First, back_populates='employees' should refer to 'employee', which is what you called the property on the related model. Second, employee = relationship('Photo' is pointing at the wrong model, it should relate to Employee.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy(app)
db.engine.echo = True
class Photo(db.Model):
id = db.Column(db.Integer, primary_key=True)
class Employee(db.Model):
id = db.Column(db.Integer, primary_key=True)
photo_id = db.Column(db.ForeignKey(Photo.id))
photo = db.relationship(Photo, backref=db.backref('employee', uselist=False))
db.create_all()
db.session.add(Employee(photo=Photo()))
db.session.commit()
print(Employee.query.get(1).photo)
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".