Selecting Rows based on multiple related tables - flask

I have the following models:
modifiers = db.Table('modifiers',
db.Column('modifier', db.Integer, db.ForeignKey('modifier.id')),
db.Column('modified_ingredient', db.Integer, db.ForeignKey('modified_ingredient.id')),
)
modified_ingredients = db.Table('modified_ingredients',
db.Column('recipe', db.Integer, db.ForeignKey('recipe.id')),
db.Column('modified_ingredient', db.Integer, db.ForeignKey('modified_ingredient.id'))
)
class Recipe(db.Model):
__tablename__ = 'recipe'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(256))
description = db.Column(db.Text)
directions = db.Column(db.Text)
prep_time = db.Column(db.Integer)
cook_time = db.Column(db.Integer)
image = db.Column(db.LargeBinary())
ingredients = db.relationship('ModifiedIngredient', secondary=modified_ingredients)
class Ingredient(db.Model):
__tablename__ = 'ingredient'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30), index=True, unique=True)
mod_ing = db.relationship("ModifiedIngredient")
class Modifier(db.Model):
__tablename__ = 'modifier'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30), index=True, unique=True)
class ModifiedIngredient(db.Model):
__tablename__ = 'modified_ingredient'
id = db.Column(db.Integer, primary_key=True)
amount = db.Column(db.Integer)
unit = db.Column(db.String(20))
ingredient = db.Column(db.Integer, db.ForeignKey('ingredient.id'))
modifiers = db.relationship('Modifier', secondary=modifiers,
backref=db.backref('modifiers', lazy='dynamic'), lazy='dynamic')
I'm trying to retrieve recipes based on having two or more ingredients.
I'm able to retreive recipes that have single ingredients with the following:
db.session.query(models.Recipe).join(models.ModifiedIngredient, models.Recipe.ingredients).join(models.Ingredient).filter(models.Ingredient.name == 'garlic')
However, adding multiple filters return 0 results. This makes sense, it's matching a single ingredient to a recipe, and an ingredient can't be both garlic and onion. So, how would I accomplish this?

The solution I have found is to use an intersect.
Issue multiple queries with each of your filter options:
q1 = db.session.query(models.Recipe).join(models.ModifiedIngredient, models.Recipe.ingredients).join(models.Ingredient).filter(models.Ingredient.name == 'garlic')
q2 = db.session.query(models.Recipe).join(models.ModifiedIngredient, models.Recipe.ingredients).join(models.Ingredient).filter(models.Ingredient.name == 'onion')
q3 = q1.intersect(q2)
And q3 will have all items that have both 'onion' and 'garlic'

Related

ForeignKey serialized as empty dict and not getting populated with data

I've two models Profile & Product representing One-Many relationship. One profile can have many products. I'm serializing all the fields. The column which has ForeignKey is coming out to be empty dictionary. The following model will make my issue more clear.
from backend_olx import db
from marshmallow import Schema, fields
from datetime import datetime
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
created_by = db.Column(db.Integer, db.ForeignKey('profile.id'), nullable=False)
purchased_by = db.Column(db.Integer, db.ForeignKey('profile.id'), nullable=True)
name = db.Column(db.String(50), nullable=False)
price = db.Column(db.Integer, nullable=False)
description = db.Column(db.Text, nullable=False)
def __repr__(self):
return '<Product Name %r>' % self.name
class Profile(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), nullable=False, unique=True)
email = db.Column(db.String(100), nullable=False, unique=True)
products_sold = db.relationship('Product', backref='profile_sold', foreign_keys="Product.created_by",lazy=True)
products_purchased = db.relationship('Product', backref='profile_purchased', foreign_keys="Product.purchased_by",lazy=True)
def __repr__(self):
return '<User %r>' % self.username
class ProfileSchema(Schema):
id = fields.Int(dump_only=True)
username = fields.Str()
email = fields.Str()
products_sold = fields.Nested('ProductSchema', many=True)
products_purchased = fields.Nested('ProductSchema', many=True)
class ProductSchema(Schema):
id = fields.Int(dump_only=True)
name = fields.Str()
price = fields.Int()
created_by = fields.Nested('ProfileSchema')
purchased_by = fields.Nested('ProfileSchema')
profile_schema = ProfileSchema()
profiles_schema = ProfileSchema(many=True)
product_schema = ProductSchema()
products_schema = ProductSchema(many=True)
The Nested() method in ProfileSchema is working as expected but it is giving { } in ProductSchema.
I want created_by and purchased_by fields to be populated as well.
How to go about ths?
You can't just pass a foreign key and expect Nested to know what to do about it. You need to pass a relation instead.
Create a relation for both fields and use the relation name in the schema.
I typically use xxx_id for column name and xxx for relation name.
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
created_by_id = db.Column(db.Integer, db.ForeignKey('profile.id'), nullable=False)
purchased_by_id = db.Column(db.Integer, db.ForeignKey('profile.id'), nullable=True)
name = db.Column(db.String(50), nullable=False)
price = db.Column(db.Integer, nullable=False)
description = db.Column(db.Text, nullable=False)
# Setup relations here
created_by = db.relationship(...)
purchased_by = db.relationship(...)
class ProductSchema(Schema):
id = fields.Int(dump_only=True)
name = fields.Str()
price = fields.Int()
created_by = fields.Nested('ProfileSchema')
purchased_by = fields.Nested('ProfileSchema')

Flask SQLAlchemy join query returning [<Service 2>]

Why is the code
res = Service.query.join(Counter,(Counter.service_id == Service.id)).filter(Service.dept_id == deptId).all()
print(res)
is always returning []. I want the data in array format.
My Models are as below. Please excuse me if the question is very primitive. I am very new to SQL Alchemy.
class Service(FlaskSerializeMixin,db.Model):
id = db.Column(db.Integer, primary_key=True)
ser_name = db.Column(db.String(100),unique=True,nullable=False)
ser_prefix = db.Column(db.String(1),unique=True,nullable=False)
cnt_num = db.Column(db.Integer,nullable=False)
pri_q_flg = db.Column(db.Boolean,nullable=False)
dept_id = db.Column(db.Integer, db.ForeignKey('department.id'),nullable=False)
cre_time =db.Column(db.DateTime)
chg_time =db.Column(db.DateTime)
__table_args__ = (
db.UniqueConstraint('ser_name', 'dept_id', name='unique_service_dept'),
)
pqueues = db.relationship('PQueue',backref='service',lazy=True)
counters = db.relationship('Counter',backref='service',lazy=True)
class Counter(FlaskSerializeMixin,db.Model):
id = db.Column(db.Integer, primary_key=True)
cnt_num = db.Column(db.Integer,nullable=False)
service_id = db.Column(db.Integer, db.ForeignKey('service.id'),nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
cre_time =db.Column(db.DateTime)
chg_time =db.Column(db.DateTime)
__table_args__ = (
db.UniqueConstraint('cnt_num', 'service_id', name='unique_cnt_service'),
)

Flask-appbuilder many-to-may relationship sqlalchemy.exc.NoReferencedTableError Error

I've read through all of these (https://stackoverflow.com/search?q=sqlalchemy.exc.NoReferencedTableError%3A), the Flask-appbuilder docs, the sqlalchemy docs, and the Flask-sqlalchemy docs and more. Unfortunately, I can't find any full examples of a many-to-many sqlalchemy relationship.
I have a python Flask app using flask-appbuilder (which relies on flask-sqlalchemy). My app/model.py file has this:
field_feature_association = Table('field_feature_association',Base.metadata,
Column('field_id', Integer, ForeignKey('field.id')),
Column('feature_id',Integer, ForeignKey('feature.id')),
schema="main"
)
class field(Model):
__tablename__ = 'field'
id = Column(Integer, primary_key=True)
name = Column(String(70), nullable=False)
database_type = Column(String(70)) #varchar(255), text, int
joinable_to = Column(Text())
notes = Column(Text()) #don't use this for X
table_id = Column(Integer, ForeignKey('table.id'))
table = relationship("table")
features = relationship("feature",
secondary = field_feature_association,
backref = backref('fields'),
)
def __repr__(self):
return self.name
class feature(Model):
__tablename__ = 'feature'
id = Column(Integer, primary_key=True)
name = Column(String(70), unique = True, nullable=False)
field_id = Column(Integer, ForeignKey('field.id'))
#field = relationship("field")
def __repr__(self):
return self.name
It's generating this error:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'field_feature_association.feature_id' could not find table 'feature' with which to generate a foreign key to target column 'id'
Thoughts on how to fix this error?
Here is a working sample of many-to-many in SQLAlchemy. Moreover I modified your model and it works fine:
field_feature_association = db.Table('field_feature_association', db.Model.metadata,
db.Column('field_id', db.Integer, db.ForeignKey('field.id')),
db.Column('feature_id', db.Integer, db.ForeignKey('feature.id')),
schema="main"
)
class Field(db.Model):
__tablename__ = 'field'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(70), nullable=False)
database_type = db.Column(db.String(70)) # varchar(255), text, int
joinable_to = db.Column(db.Text())
notes = db.Column(db.Text()) # don't use this for X
features = db.relationship("Feature",
secondary=field_feature_association,
backref=db.backref('fields'),
)
def __repr__(self):
return self.name
class Feature(db.Model):
__tablename__ = 'feature'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(70), unique=True, nullable=False)
field_id = db.Column(db.Integer, db.ForeignKey('field.id'))
# field = relationship("field")
def __repr__(self):
return self.name
and this is how to use it:
field = Field()
field.name="filed1"
feature = Feature()
feature.name = "feature1"
field.features.append(feature)
db.session.add(field)
db.session.commit()
My database object is imported as ’db’ and I have used it explicitely to refer to other types.

Self-Referential Association Relationship SQLalchemy

In my flask application with flask-sqlalchemy i need to create association between two contact
here is my Contact model
class Contact(db.Model):
__tablename__ = 'contact'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Unicode(120), nullable=False, unique=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
to_contacts = db.relationship('Contact',
secondary='ContactRelation',
primaryjoin='id==contactrelation.c.from_contact_id',
secondaryjoin='id==contactrelation.c.to_contact_id',
backref='from_contacts')
and my association class ContactRelation:
class ContactRelation(db.Model):
__tablename__ = 'contactrelation'
id = db.Column(db.Integer, primary_key=True)
from_contact_id = db.Column(db.Integer, db.ForeignKey('contact.id'))
to_contact_id = db.Column(db.Integer, db.ForeignKey('contact.id'))
relation_type = db.Column(db.String(100), nullable=True)
i have error :
AttributeError: type object 'ContactRelation' has no attribute 'c'
Thanks to Michel and Simon on SQLAlchemy mailing list i need association_proxy and two relation to Contact relation.
class Contact(db.Model):
__tablename__ = 'contact'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Unicode(120), nullable=False, unique=False)
created_on = db.Column(db.DateTime, default=datetime.utcnow)
birthday = db.Column(db.DateTime)
background = db.Column(db.Text)
photo = db.Column(db.Unicode(120))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
to_contacts = association_proxy('to_relations', 'to_contact')
from_contacts = association_proxy('from_relations', 'from_contact')
class ContactRelation(db.Model):
__tablename__ = 'contactrelation'
id = db.Column(db.Integer, primary_key=True)
from_contact_id = db.Column(db.Integer, db.ForeignKey('contact.id'))
to_contact_id = db.Column(db.Integer, db.ForeignKey('contact.id'))
relation_type = db.Column(db.String(100), nullable=True)
from_contact = db.relationship(Contact,
primaryjoin=(from_contact_id == Contact.id),
backref='to_relations')
to_contact = db.relationship(Contact,
primaryjoin=(to_contact_id == Contact.id),
backref='from_relations')
Self-referential many-to-many relationship with Association Object.
User Class:
class User(Base):
__tablename__ = "User"
id = Column(String(36), primary_key=True, default=lambda : str(uuid1()))
Association Class:
class UserIgnore(Base):
__tablename__ = "UserIgnore"
id = Column(String(36), primary_key=True, default=lambda : str(uuid1()))
ignored_by_id = Column("ignored_by_id", String(36), ForeignKey("User.id"), primary_key=True)
ignored_by = relationship("User", backref="ignored_list", primaryjoin=(User.id == ignored_by_id))
ignored_id = Column("ignored_id", String(36), ForeignKey("User.id"), primary_key=True)
ignored = relationship("User", backref="ignored_by_list", primaryjoin=(User.id == ignored_id))
Access the relationship objects with
someUser.ignored_list
or
someUser.ignored_by_list
Thanks to Sean
Your relationship is not correctly designed. A secondary should be an ordinary table, not a mapped class. If you want the extra data (relation_type) on your ContactRelation, you should use the Association Table pattern described in the SQLAlchemy Relationship docs: http://docs.sqlalchemy.org/en/rel_1_1/orm/basic_relationships.html#association-object
it seems that if you change the to_contacts to something like below, your problem will be solved:
to_contacts = db.relationship('Contact',
secondary='ContactRelation',
primaryjoin='id==contactrelation.from_contact_id',
secondaryjoin='id==contactrelation.to_contact_id',
backref='from_contacts')

Flask SQLAlchemy - how to organize the db Models

To create new tables in the db I write something like this:
class Cat(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(40))
age = db.Column(db.String(40))
born_date = db.Column(db.DateTime, default=datetime.datetime.utcnow)
is_feline = db.Column(db.Boolean)
class Dog(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(40))
age = db.Column(db.String(40))
born_date = db.Column(db.DateTime, default=datetime.datetime.utcnow)
wave_tail = db.Column(db.Boolean)
With only 2 tables created. Cat and Dog. Classic one, from tutorials.
I want do to something like this
class Animal(object):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(40))
age = db.Column(db.String(40))
born_date = db.Column(db.DateTime, default=datetime.datetime.utcnow)
common_functions
class Cat extends Animal:
is_feline = db.Column(db.Boolean)
def override common_functions
def specific functions
class Dog extends Animal:
wave_tail = db.Column(db.Boolean)
with only 2 tables created. Cat and Dog.
Or is there a place where I can learn this. The SQLAlchemy documentation is huge...
Inherit your entities from SQLAlchemy Model class and the parent class at the same time:
class Animal(object):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(40))
age = db.Column(db.String(40))
born_date = db.Column(db.DateTime, default=datetime.datetime.utcnow)
def hi(self):
print 'hello!'
class Cat(Animal, db.Model):
__tablename__ = 'cats'
is_feline = db.Column(db.Boolean)
def hi(self):
print 'cat'
def hello(self):
print 'hello'
class Dog(Animal, db.Model):
__tablename__ = 'dogs'
wave_tail = db.Column(db.Boolean)