I am creating a website using Flask and SQLAlchemy. This website keeps track of classes that a student has taken. I would like to find a way to search my database using SQLAlchemy to find all unique classes that have been entered. Here is code from my models.py for Class:
class Class(db.Model):
__tablename__ = 'classes'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100))
body = db.Column(db.Text)
created = db.Column(db.DateTime, default=datetime.datetime.now)
user_email = db.Column(db.String(100), db.ForeignKey(User.email))
user = db.relationship(User)
In other words, I would like to get all unique values from the title column and pass that to my views.py.
Using the model query structure you could do this
Class.query.with_entities(Class.title).distinct()
query = session.query(Class.title.distinct().label("title"))
titles = [row.title for row in query.all()]
titles = [r.title for r in session.query(Class.title).distinct()]
As #van has pointed out, what you are looking for is:
session.query(your_table.column1.distinct()).all(); #SELECT DISTINCT(column1) FROM your_table
but I will add that in most cases, you are also looking to add another filter on the results. In which case you can do
session.query(your_table.column1.distinct()).filter_by(column2 = 'some_column2_value').all();
which translates to sql
SELECT DISTINCT(column1) FROM your_table WHERE column2 = 'some_column2_value';
Related
I have two tables, Products and Orders, inside my Flask-SqlAlchemy setup, and they are linked so an order can have several products:
class Products(db.Model):
id = db.Column(db.Integer, primary_key=True)
....
class Orders(db.Model):
guid = db.Column(db.String(36), default=generate_uuid, primary_key=True)
products = db.relationship(
"Products", secondary=order_products_table, backref="orders")
....
linked via:
order_products_table = db.Table("order_products_table",
db.Column('orders_guid', db.String(36), db.ForeignKey('orders.guid')),
db.Column('products_id', db.Integer, db.ForeignKey('products.id'))
# db.Column('license', dbString(36))
)
For my purposes, each product in an order will receive a unique license string, which logically should be added to the order_products_table rows of each product in an order.
How do I declare this third license column on the join table order_products_table so it gets populated it as I insert an Order?
I've since found the documentation for the Association Object from the SQLAlchemy docs, which allows for exactly this expansion to the join table.
Updated setup:
# Instead of a table, provide a model for the JOIN table with additional fields
# and explicit keys and back_populates:
class OrderProducts(db.Model):
__tablename__ = 'order_products_table'
orders_guid = db.Column(db.String(36), db.ForeignKey(
'orders.guid'), primary_key=True)
products_id = db.Column(db.Integer, db.ForeignKey(
'products.id'), primary_key=True)
order = db.relationship("Orders", back_populates="products")
products = db.relationship("Products", back_populates="order")
licenses = db.Column(db.String(36), nullable=False)
class Products(db.Model):
id = db.Column(db.Integer, primary_key=True)
order = db.relationship(OrderProducts, back_populates="order")
....
class Orders(db.Model):
guid = db.Column(db.String(36), default=generate_uuid, primary_key=True)
products = db.relationship(OrderProducts, back_populates="products")
....
What is really tricky (but also shown on the documentation page), is how you insert the data. In my case it goes something like this:
o = Orders(...) # insert other data
for id in products:
# Create OrderProducts join rows with the extra data, e.g. licenses
join = OrderProducts(licenses="Foo")
# To the JOIN add the products
join.products = Products.query.get(id)
# Add the populated JOIN as the Order products
o.products.append(join)
# Finally commit to database
db.session.add(o)
db.session.commit()
I was at first trying to populate the Order.products (or o.products in the example code) directly, which will give you an error about using a Products class when it expects a OrderProducts class.
I also struggled with the whole field naming and referencing of the back_populates. Again, the example above and on the docs show this. Note the pluralization is entirely to do with how you want your fields named.
First of all, i would like to apologize as my SQL knowledge level is still very low. Basically the problem is the following: I have two distinct tables, no direct relationship between them, but they share two columns: storm_id and userid.
Basically, i would like to query all posts from storm_id, that are not from a banned user and some extra filters.
Here are the models:
Post
class Post(db.Model):
id = db.Column(db.Integer, primary_key = True)
...
userid = db.Column(db.String(100))
...
storm_id = db.Column(db.Integer, db.ForeignKey('storm.id'))
Banneduser
class Banneduser(db.Model):
id = db.Column(db.Integer, primary_key=True)
sn = db.Column(db.String(60))
userid = db.Column(db.String(100))
name = db.Column(db.String(60))
storm_id = db.Column(db.Integer, db.ForeignKey('storm.id'))
Both Post and Banneduser are another table (Storm) children. And here is the query i am trying to output. As you can see, i am trying to filter:
verified posts
by descending order
with a limit (i put it apart from the query as the elif has other filters)
# we query banned users id
bannedusers = db.session.query(Banneduser.userid)
# we do the query except the limit, as in the if..elif there are more filtering queries
joined = db.session.query(Post, Banneduser)\
.filter(Post.storm_id==stormid)\
.filter(Post.verified==True)\
# here comes the trouble
.filter(~Post.userid.in_(bannedusers))\
.order_by(Post.timenow.desc())\
try:
if contentsettings.filterby == 'all':
posts = joined.limit(contentsettings.maxposts)
print((posts.all()))
# i am not sure if this is pythonic
posts = [item[0] for item in posts]
return render_template("stream.html", storm=storm, wall=posts)
elif ... other queries
I got two problems, one basic and one underlying problem:
1/ .filter(~Post.userid.in_(bannedusers))\ gives one output EACH TIME post.userid is not in bannedusers, so i get N repeated posts. I try to filter this with distinct, but it does not work
2/ Underlying problem: i am not sure if my approach is the correct one (the ddbb model structure/relationship plus the queries)
Use SQL EXISTS. Your query should be like this:
db.session.query(Post)\
.filter(Post.storm_id==stormid)\
.filter(Post.verified==True)\
.filter(~ exists().where(Banneduser.storm_id==Post.storm_id))\
.order_by(Post.timenow.desc())
Lets say I have SQL Alchemy ORM classes:
class Session(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_agent = db.Column(db.Text, nullable=False)
class Run(db.Model):
id = db.Column(db.Integer, primary_key=True)
session_id = db.Column(db.Integer, db.ForeignKey('session.id'))
session = db.relationship('Session', backref=db.backref('runs', lazy='dynamic'))
And I want to query for essentially the following:
((session.id, session.user_agent, session.runs.count())
for session in Session.query.order_by(Session.id.desc()))
However, this is clearly 1+n queries, which is terrible. What is the correct way to do this, with 1 query? In normal SQL, I would do this with something along the lines of:
SELECT session.id, session.user_agent, COUNT(row.id) FROM session
LEFT JOIN rows on session.id = rows.session_id
GROUP BY session.id ORDER BY session.id DESC
Construct a subquery that groups and counts session ids from runs, and join to that in your final query.
sq = session.query(Run.session_id, func.count(Run.session_id).label('count')).group_by(Run.session_id).subquery()
result = session.query(Session, sq.c.count).join(sq, sq.c.session_id == Session.id).all()
The targeted SQL could be produced simply with:
db.session.query(Session, func.count(Run.id)).\
outerjoin(Run).\
group_by(Session.id).\
order_by(Session.id.desc())
I developing simple blog with tagging support. Actually I would like to add tags cloud functionality and I need to get count of each tag used in blog.
My Blog and Tag models looks like:
class Blog(db.Model, ObservableModel):
__tablename__ = "blogs"
id = db.Column(db.Integer, db.Sequence('blog_id_seq'), primary_key=True)
title = db.Column(db.String(200), unique=True, nullable=True)
tags = relationship('Tag', secondary=tags_to_blogs_association_table)
class Post(db.Model, ObservableModel):
__tablename__ = "posts"
......................
blog = relationship('Blog', backref = db.backref('blogs', lazy='dynamic'))
tags = relationship('Tag', secondary=tags_to_posts_association_table)
class Tag(db.Model):
__tablename__ = "tags"
id = db.Column(db.Integer, db.Sequence('post_id_seq'), primary_key=True)
title = db.Column(db.String(30), unique=False, nullable=True)
I want to collect dictionary of pairs like tag_name : count and only one way is to iterate over Blog.tags collection with retrieving posts which contains tag item.
Actually I am not sure that it is the best (from performance point of view) solution, maybe flask-sqlalchemy provides join function?
Question: how to implement in Python using Flask-SQLAlchemy query like following:
select
t.id,
t.title,
count(post_id)
from tags t
join tags_to_blogs b on t.id=b.tag_id
join tags_to_posts p on t.id=p.tag_id
group by (t.id)
having b.blog_id=1
Try this:
query = db.session.query(Tag, db.count(Post.id))
query = query.filter(
(tags_to_posts_association_table.tag_id == Tag.id) & \
(tags_to_posts_association_table.post_id == Post.id)
)
query = query.group_by(Tag.id)
This generates this query:
SELECT tags.id AS tags_id, tags.title AS tags_title, count(posts.id) AS count_1
FROM tags, posts, tags_to_posts
WHERE tags_to_posts.tag_id = tags.id AND tags_to_posts.post_id = posts.id GROUP BY tags.id
A cleaner way could be something like this:
query = db.session.query(Tag, db.func.count(Post.id))
# This works but the preferred way is what's below it
#query = query.join(tags_to_posts_association_table, Post)
query = query.join(Post.tags)
query = query.group_by(Tag.id)
This generates this query:
SELECT tags.id AS tags_id, tags.title AS tags_title, count(posts.id) AS count_1
FROM tags INNER JOIN tags_to_posts ON tags.id = tags_to_posts.tag_id INNER JOIN posts ON posts.id = tags_to_posts.post_id GROUP BY tags.id
All these produce the same result, and you can chain them just like this:
query = db.session.query(Tag.title, db.func.count(Post.id)).join(Post.tags).group_by(Tag.id)
# This will give you a dictionary with keys the tag titles, and values the count of each
# Because you can iterate over the query, which will give you the results
# Or you can use query.all() and use it as you prefer.
results = dict(query)
Also, I'm not sure if it's db.func.count or db.count. In any way you can always from sqlalchemy import func and use func.count.
I would do it this way (pseudo code, can't remember the proper alchemy syntax but you should be able to 'convert' it quiet easily)
tags = Tags.findAll()
for tag in tags:
myDict[tag] = Post.find(tags=tag).count()
And at the and you should have all tags in myDict with their count
I need help creating SqlAlchemy query.
I'm doing a Flask project where I'm using SqlAlchemy. I have created 3 tables: Restaurant, Dish and restaurant_dish in my models.py file.
restaurant_dish = db.Table('restaurant_dish',
db.Column('dish_id', db.Integer, db.ForeignKey('dish.id')),
db.Column('restaurant_id', db.Integer, db.ForeignKey('restaurant.id'))
)
class Restaurant(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(64), index = True)
restaurant_dish = db.relationship('Dish', secondary=restaurant_dish,
backref=db.backref('dishes', lazy='dynamic'))
class Dish(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(64), index = True)
info = db.Column(db.String(256), index = True)
I have added data to the restaurant_dish table and it should be working correctly. Where I need help is understanding how to correctly get a Dish using Restaurant. Raw SQL would be something like this:
SELECT dish_id FROM restaurant_dish WHERE restaurant_id == id
What I have managed to get done but not working:
x = Restaurant.query.filter_by(Restaurant.restaurant_dish.contains(name)).all()
Thanks for help and I also appreciate tutorials that can point me in the right direction(the official documentation goes over my head).
The semantic of the relationship doesn't look right. I think it should be something like:
class Restaurant(db.Model):
...
dishes = db.relationship('Dish', secondary=restaurant_dish,
backref=db.backref('restaurants'))
Then, to retrieve all the dishes for a restaurant, you can do:
x = Dish.query.filter(Dish.restaurants.any(name=name)).all()
This should generate a query like:
SELECT dish.*
FROM dish
WHERE
EXISTS (
SELECT 1
FROM restaurant_dish
WHERE
dish.id = restaurant_dish.dish_id
AND EXISTS (
SELECT 1
FROM restaurant
WHERE
restaurant_dish.restaurant_id = restaurant.id
AND restaurant.name = :name
)
)