SqlAlchemy access column from a relationship-table - sqlalchemy

If I have a model with a many-to-many relationship und want to save specific data to the relationship, how can I access the column in the relationship table?
F. e.
borrow = db.Table('borrow',
db.Column('book_id', db.Integer, db.ForeignKey('book.id', ondelete="CASCADE"), primary_key=True),
db.Column('person_id', db.Integer, db.ForeignKey('person.id', ondelete="CASCADE"), primary_key=True),
db.Column('deadline', db.Date, nullable=False))
class Book(Base):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False, unique=True)
class Person(Base):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False, unique=True)
borrow = db.relationship('Book', secondary=borrow, lazy='subquery', backref=db.backref('person', lazy=true))
book = Book(name="MyBook")
person = Person(name="John")
person.borrow.append(book)
But how can I save or access the deadline? The column is in my db, but I can't use it in the code
Thank you

Refactor Borrow as a class, like you have already done with Book and Person, and then you can set the deadline when you instantiate an instance of Borrow.
See an example from the docs here.

Related

Import data from a joined table as a current (readable) column in SQLAlchemy?

I have this schema:
class Company(db.Model):
__tablename__ = 'companies'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=True, default=None)
domain = db.Column(db.String(250), nullable=True, default=None)
organization_id = db.Column(db.Integer, db.ForeignKey('organizations.id'), nullable=False)
class Contact(db.Model):
__tablename__ = 'contacts'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=True, default=None)
email = db.Column(db.String(250), nullable=False)
company_id = db.Column(db.Integer, db.ForeignKey('companies.id'), nullable=True, default=None)
company = relationship('Company')
organization_id = db.Column({Import Company.organization_id as eager})
The last line is of course garbage, but it's to show the idea:
I'd like to have the value "organization_id" available in Contact, even though it's not present in the table "contacts", but since it's present in "companies", is there a way to ask SQLAlchemy to load the value from "companies" via a JOIN, and affect it to "contacts" as a read-only value?
That way, when I search for a contact, for instance :
contact = Contact.query.filter(Contact.email = 'test#test.com').first()
print(contact.organization_id) # => 1
Thank you.
You can use the hybrid_property decorator to define an attribute on your class:
class Contact(db.Model):
...
#hybrid_property
def organization_id(self):
return self.company.organization_id if self.company else None
Using contact.organization_id will load the company using the foreign key relationship.

SqlAlchemy error: Foreign key could not find table

So I'm building an app and I'm trying to save new changes to my database but when I try to commit the changes in the flask using db.session.commit() it returns me the following error:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'products.country_id' could not find table 'countries' with which to generate a foreign key to target column 'id'
In my models.py I have the following:
from app import db
from . import db
from datetime import datetime
def now():
return datetime.now()
class Countries(db.Model):
__tablename__ = 'countries'
__table_args__ = {'schema': 'products_data'}
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
name = db.Column(db.String(255))
code = db.Column(db.String(45))
def __repr__(self):
return f'Id {self.id}'
class Categories(db.Model):
__tablename__ = 'categories'
__table_args__ = {'schema': 'products_data'}
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
name = db.Column(db.String(255))
def __repr__(self):
return f'Id {self.id}'
class Brands(db.Model):
__tablename__ = 'brands'
__table_args__ = {'schema': 'products_data'}
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
name = db.Column(db.String(255))
logo = db.Column(db.String(5000))
feed = db.Column(db.String(5000))
feed_type = db.Column(db.String(45))
category_id = db.Column(db.Integer, db.ForeignKey('categories.id'))
country_id = db.Column(db.Integer, db.ForeignKey('countries.id'))
awinmid = db.Column(db.Integer)
def __repr__(self):
return f'Id {self.id}'
class Products(db.Model):
__tablename__ = 'products'
__table_args__ = {'schema': 'products_data'}
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
name = db.Column(db.String(255))
url = db.Column(db.Text)
category_id = db.Column(db.Integer, db.ForeignKey('categories.id'))
country_id = db.Column(db.Integer, db.ForeignKey('countries.id'))
price = db.Column(db.Float)
currency = db.Column(db.String(45))
discount_price = db.Column(db.Float)
shipping = db.Column(db.Float)
brand_id = db.Column(db.Integer, db.ForeignKey('brands.id'))
Am I doing anything wrong when associating a column in products with a foreign key? This is the first time I encounter this error so I'm really lost on what to do right now.
To fix I just added the schema to the db.ForeignKey and it worked
Example:
db.ForeignKey('products_data.countries.id')
PS:
Not my idea. Just wanted to post the answer in case someone visits the post later with the same problem.
Gord Thompson thanks for the help!
First of all, I don t see any table Categories. Secondly, you copy pasted your schema from the Products table into your Countries one.
PS: By default sqlalchemy gives the tables the name of the class (lower cased). So your __tablename__='products' does nothing actually.
EDIT:
The problem with your code lies in how you set the __table_args__ attribute. You assign an object to it, which by their specifications is wrong.
Take a look at the following example and modify your code accordingly
__table_args__ = ({'schema': 'products_data'})
Also for further reference, take a look at this https://docs.sqlalchemy.org/en/13/orm/extensions/declarative/table_config.html

SQLAlchemy: Trouble querying in manyto many relationship

New to SQLalchemy, an sql queries in general but hopefully this will be clear to someone :) In a Flask application, I have two models, User and classes, in a many to many relationship
Here is the models.py
user_to_classes = db.Table('user_to_classes', Base.metadata,
db.Column('class_id', db.Integer, db.ForeignKey('classes.id')),
db.Column('user_id', db.Integer, db.ForeignKey('users.id'))
)
class Classes(db.Model):
__tablename__= 'classes'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64))
date = db.Column(db.DateTime)
participants = db.relationship('User', secondary=user_to_classes, backref = db.backref('classes',lazy='dynamic'))
classtype_id = db.Column(db.Integer, db.ForeignKey('classtype.id'))
status = db.Column(db.Integer) #1 = open, 0 = closed
class User(UserMixin,db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
email = db.Column(db.String(64),unique=True,index=True)
firstname = db.Column(db.String(64))
lastname = db.Column(db.String(64))
fullname = db.Column(db.String(64), index=True)
telephone = db.Column(db.String(64))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
password_hash = db.Column(db.String(128))
member_since = db.Column(db.DateTime(), default=datetime.utcnow)
last_seen = db.Column(db.DateTime(), default=datetime.utcnow)
notes = db.Column(db.Text())
punchcard_passes = db.Column(db.Integer)
I am trying to know how many classes attended a user. I have no problem querying how many users participated in a class, but not the reverse as there is no value to query in the user model. Is it even possible? Not being fluent in SQL queries, I am not sure what to search for either on google. All the exemples I have seen do a one way query, and never the other way.
thanks!
How about len(user.classes) ? Doesn't it work ?
In addition: don't name a class in plural, since an object of it represents only one class.

sql alchemy: return all unique types of great great grandchildren

I have 6 tables. I am essentially trying to return all unique types of great great grandchildren
How do I return a list of all the unique types of sku_numbers in a FreightDomesticOrder?
Table Definitions:
class FreightOrderDomestic(db.Model):
"""
A shipment of products from a manufacturer to a fulfillment center
"""
__tablename__ = 'Freight_Order_Domestic'
id = db.Column(db.Integer, primary_key=True, nullable=False)
class Pallet(db.Model):
"""
An individual Pallet (full of individual cases)
"""
__tablename__ = 'Pallet'
id = db.Column(db.Integer, primary_key=True, nullable=False)
freight_order_fkey = db.ForeignKey("Freight_Order_Domestic.id")
freight_order_id = db.Column(db.Integer, freight_order_fkey, nullable=False)
class OuterCase(db.Model):
"""
An outer case (full of inner cases)
"""
__tablename__ = 'Outer_Case'
id = db.Column(db.Integer, primary_key=True, nullable=False)
pallet_fkey = db.ForeignKey("Pallet.id")
pallet_id = db.Column(db.Integer, pallet_fkey, nullable=False)
class InnerCase(db.Model):
"""
An individual case (full of individual items)
"""
__tablename__ = 'Inner_Case'
id = db.Column(db.Integer, primary_key=True, nullable=False)
outer_case_fkey = db.ForeignKey("Outer_Case.id")
outer_case_id = db.Column(db.Integer, outer_case_fkey, nullable=False)
class Each(db.Model):
"""
An individual item
"""
__tablename__ = 'Each'
id = db.Column(db.Integer, primary_key=True, nullable=False)
inner_case_fkey = db.ForeignKey("Inner_Case.id")
inner_case_id = db.Column(db.Integer, inner_case_fkey, nullable=False)
sku_fkey = db.ForeignKey("Sku.id")
sku_id = db.Column(db.Integer, sku_fkey, nullable=False)
class Sku(db.Model):
"""
The SKU of an product, the attributes it should have to determine pricing
"""
__tablename__ = 'Sku'
id = db.Column(db.Integer, primary_key=True, nullable=False)
sku_number = db.Column(db.String(255), nullable=False)
Here is what I am trying so far but I am stuck, I am also wondering how cheap I can make this:
SKUs = Session.query(Pallet, Outer_case, Inner_case, Each, Sku).filter(Pallet.id == Outer_case.pallet_id).filter(Outer_case.id == Inner_case.outer_case_id).filter(Inner_case.id == Each.inner_case_id).filter(Each.sku_id == sku.id).all()
My other idea was to loop through all Pallets and then Outer_cases and so on but that seems too expensive.
Edited post after table definitions:
Given your table definitions, this should work:
SKUs = session.query(Sku.sku_number)
.join(Each).join(InnerCase)
.join(OuterCase).join(Pallet)
.join(FreightOrderDomestic)
.filter(FreightOrderDomestic.id == myOrderNumber)
.group_by(Sku).all()
However, looking at your table definitions I have some other comments that will hopefully help:
You should setup relationships between the tables, so you can easily work with the different objects. Check out the sqlalchemy documentation on relationships here
I would suggest reading up on Database Normalization. This will help you understand some of the below points
You currently have Each setup so there will be duplicate items of the same type if they are assigned to different InnerCases. This is not a good database practice. You should setup this relationship as a many to many relationship, and you can read about that here. This will allow you to have a list of items, and each item can link to many different InnerCases
FreightDomesticOrder should have a column for order number. You don't want to use a key value as an order number
If you are going to handle international orders also, you should probably just create a type field for FreightOrderDomestic and rename it to FreightOrder
Original Post:
Can you provide the table definitions? This is very hard to answer accurately without seeing the relationships you have setup. Something like this could work, if you setup your tables like I would have given the description you gave, or it could not work because you didn't provide enough info:
SKUs = session.query(Sku.sku_number)
.join(Each).join(Inner_case)
.join(Outer_case).join(Pallet)
.join(Freight_order)
.filter(Freight_order.order_number == myOrderNumber)
.group_by(Sku).all()

Querying multiple joined inherited tables filtered by a many-to-many relationship

I have an SQLAlchemy scheme that looks roughly like this:
participation = db.Table('participation',
db.Column('artist_id', db.Integer, db.ForeignKey('artist.id'),
primary_key=True),
db.Column('song_id', db.Integer, db.ForeignKey('song.id'),
primary_key=True),
)
class Streamable(db.Model):
id = db.Column(db.Integer, primary_key=True)
kind = db.Column(db.String(10), nullable=False)
score = db.Column(db.Integer, nullable=False)
__mapper_args__ = {'polymorphic_on': kind}
class Artist(Streamable):
id = db.Column(db.Integer, db.ForeignKey('streamable.id'), primary_key=True)
name = db.Column(db.Unicode(128), nullable=False)
__mapper_args__ = {'polymorphic_identity': 'artist'}
class Song(Streamable):
id = db.Column(db.Integer, db.ForeignKey('streamable.id'), primary_key=True)
name = db.Column(db.Unicode(128), nullable=False)
artists = db.relationship("Artist", secondary=participation,
backref=db.backref('songs'))
__mapper_args__ = {'polymorphic_identity': 'song'}
class Video(Streamable):
id = db.Column(db.Integer, db.ForeignKey('streamable.id'), primary_key=True)
song_id = db.Column(db.Integer, db.ForeignKey('song.id'), nullable=False)
song = db.relationship('Song', backref=db.backref('videos', lazy='dynamic'),
primaryjoin="Song.id==Video.song_id")
__mapper_args__ = {'polymorphic_identity': 'video'}
I'd like to do a single query for Songs or Videos that have a particular artist; i.e., these two queries in one query (all queries should be .order_by(Streamable.score)):
q1=Streamable.query.with_polymorphic(Video)
q1.join(Video.song, participation, Artist).filter(Artist.id==1)
q2=Streamable.query.with_polymorphic(Song)
q2.join(participation, Artist).filter(Artist.id==1)
Here's the best I reached; it emits monstrous SQL and always yields empty results (not sure why):
p1=db.aliased(participation)
p2=db.aliased(participation)
a1=db.aliased(Artist)
a2=db.aliased(Artist)
q=Streamable.query.with_polymorphic((Video, Song))
q=q.join(p1, a1).join(Video.song, p2, a2)
q.filter(db.or_((a1.id==1), (a2.id==1))).order_by('score')
What's the right way to do this query, if at all (maybe a relational datastore is not the right tool for my job...)?
Your queries are basically right. I think the change from join to outerjoin should solve the problem:
q=q.outerjoin(p1, a1).outerjoin(Video.song, p2, a2)
I would also replace the order_by with:
q = q.order_by(Streamable.score)