I'm using Flask-SQLAlchemy and flask on server side.
I want to create a relationship between User(Parent), Category(Child) and item(Subchild).
I don't know if a parent-child-subchild is right way to approach this but my end goal is to easily fetch all the items rated by user in each category.
Following is one of the cases i tried but it doesn't work, probably because i'm doing something wrong.
I have an item table with item category(such as fiction, non-fiction, crime book). each category have 5 items(books). I want to let user rate five items from each category. So, i have following table.
class User(db.Model, UserMixin):
__tablename__ = "users"
id = db.Column('user_id',db.Integer, primary_key=True)
active = db.Column(db.Boolean(), nullable=False, default=False)
username = db.Column(db.String(50), nullable=False, unique=True)
password = db.Column(db.String(255), nullable=False, default='')
# do we need unique=True in any column
#let's create class for item(child) and category(parent)
class Rated_item(db.Model):
__tablename__ = 'item'
id = db.Column(db.Integer, primary_key=True)
item_id = db.Column(db.Integer)
category_id = db.Column(db.Integer)
quality = db.Column(db.String(50))
taste = db.Column(db.String(50))
user_id = db.Column(db.Integer, db.ForeignKey('users.user_id'))
# evaluation_status is set to True, once a user finishes an item.
evaluation_status = db.Column(db.Boolean, unique=False, default=False)
Since i'm working with flask-jinja based webpage, every-time a user logs in, i need to send status of user i.e how many categories have been rated by user and how many items have been rated in each category.
I tried following but i think i'm doing something conceptually wrong.
status=Rated_item.query.filter_by(user_id = current_user.id).order_by(Rated_item.pub_date.desc()).all()
Any suggestion how should i improve my table or query to get all categories (and all items in each category) rated by user.
''' items rated by user '''
status1 = Rated_item.query.func.count(distinct(item_id)).filter_by(user_id == current_user.id)
'''categories rated by user'''
status2 = Rated_item.query.func.count(distinct(category_id)).filter_by(user_id == current_user.id)
I guess this query would help you.
Related
I have three models...
class Customer(db.Model, TimestampMixin):
pk = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Text, nullable=False)
class User(db.Model, TimestampMixin):
pk = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Text, nullable=False)
class CustomerUserStat(db.Model):
__table_args__ = (
UniqueConstraint("customer_pk", "user_pk", name="customer_pk_user_pk"),
)
pk = db.Column(db.Integer, primary_key=True)
customer_pk = db.Column(db.Integer, db.ForeignKey("customers.pk"), nullable=False)
user_pk = db.Column(db.Integer, db.ForeignKey("users.pk"), nullable=False)
data = db.Column(db.Integer, nullable=False, default=0)
I’ve tried...
m.Customer.query.outerjoin(
m.CustomerUserStat,
(m.CustomerUserStat.user_pk == entity.pk)
& (m.CustomerUserStat.customer_pk == m.Customer.pk)
).add_entity(m.CustomerUserStat)
But that gives me a tuple with a Customer object and a CustomerUserStat (or None if one doesn’t exist), close but not quite what I am looking for.
I’ve also tried adding a userstats relationship to the Customer model and ...
m.Customer.query.outerjoin(m.Customer.userstats
).filter(m.CustomerUserStat.user_pk == user.pk
).options(contains_eager(m.Customer.userstats))
But that didn’t produce any results if the user's CustomerUserStat record was missing.
Ultimately, I would like to write a query that will give me a list of Customer objects with an attribute that has an instance of CustomerUserStat for a user that I specify in the query and I need to be able to order by the CustomerUserStat.data field and paginate the results.
It looks to me like you are looking for the AssociationProxy pattern
I came up with this hack to get the query that I needed. In my Flask view function, I defined a new class that inherits from the original and added the relationship to it. Then I use the new class to create the query.
class TempCustomer(m.Customer):
userstats = db.relationship(
m.CustomerUserStat,
primaryjoin=(m.Customer.pk == m.CustomerUserStat.customer_pk) &
(m.CustomerUserStat.user_pk == agent.pk),
uselist=False,
viewonly=True,
lazy="joined",
)
q = TempCustomer.query.outerjoin(m.CustomerUserStat, (m.Customer.pk ==
m.CustomerUserStat.customer_pk) & (m.CustomerUserStat.user_pk == agent.pk),)
This works for me, but doesn't seem like the best solution.
It also results in two LEFT JOINS for the same data which is probably not the most efficient.
Hi i have 3 models in my django projects, when user send request i have his dealer_id i need return query set with info about material from Material table and for each row add discount_percent from last model where dealer_id = current dealer_id and row id. Please help me if you have answer.
models
class Material(models.Model):
id = models.AutoField(primary_key=True)
color = models.IntegerField()
name = models.CharField(max_length=100)
material_width = models.IntegerField()
price = models.BigIntegerField(default=0)
class Dealer(models.Model):
id = models.AutoField(primary_key=True)
dealer_name = models.CharField(max_length=100)
dealer_phone = models.CharField(max_length=13, unique=True)
dealer_email = models.CharField(max_length=150, null=False, unique=True)
dealer_firm_name = models.CharField(max_length=100, null=True)
dealer_address = models.CharField(max_length=100, null=True)
dealer_unp = models.CharField(max_length=9, null=True)
dealer_amount = models.FloatField(default=0.0)
user_id = models.BigIntegerField(unique=True)
class MaterialDealerPrice(models.Model):
id = models.AutoField(primary_key=True)
dealer_id = models.BigIntegerField(null=False)
material_id = models.BigIntegerField(null=False)
discount = models.FloatField(null=True, default=0.0)
This looks like a set of models that were automatically created by running inspectdb. You should always treat that output as a first draft; there is a lot that needs to be done manually to tidy it up.
Firstly, your MaterialDealerPrice model needs to have foreign keys to Dealer and Material:
class MaterialDealerPrice(models.Model):
dealer = models.ForeignKey('Dealer', null=False)
material = models.ForeignKey('Material', null=False)
discount = models.FloatField(null=True, default=0.0)
Secondly, you should recognise that this model is in fact the through table of a many-to-many relationship.
class Material(models.Model):
...
dealers = models.ManyToManyField('Dealer', through='MaterialDealerPrice')
Now, you should be able to follow these relationships in your query. Unfortunately your question is not clear enough to know what you actually want to do; you should give an example of the desired output.
Annotate can be used for this purpose. https://docs.djangoproject.com/en/1.11/topics/db/aggregation/
I'm trying to count the number of items in their respective categories and end up with a collection that I can iterate through in a jinja template. My final output is something like:
category1, 5
category2, 10
category3, 0
The zero items case is important.
My model is:
class Category(Base):
__tablename__ = 'category'
id = Column(Integer, primary_key=True)
name = Column(String(80), unique=True)
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship(User)
class Item(Base):
__tablename__ = 'item'
id = Column(Integer, primary_key=True)
name = Column(String(80))
description = Column(String(500))
category_id = Column(Integer, ForeignKey('category.id'))
category = relationship(Category)
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship(User)
date_added = Column(DateTime, default=datetime.datetime.now)
I have been kindly pointed in the direction of Stackoverflow: Counting relationships in SQLAlchemy, which led me to the query
count_categories = db_session.query(Category.name, func.count(Item.id)).join(Item.category).group_by(Category.id).all()
Which is almost correct, but it does not handle the zero case. When a category has zero items, I still need the category returned by the query.
Any help, much appreciated.
Actually, I've figured it out:
count_categories = db_session.query(
Category.name, func.count(Item.id)).outerjoin(
Item).group_by(Category.id).all()
See SQLAlchemy documentation on Joins
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.
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()