Flask_sqlalchemy many to many query - flask

I have created the following models:
tag_post = db.Table('tag_post',
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'), primary_key=True),
db.Column('post_id', db.Integer, db.ForeignKey('post.id'), primary_key=True))
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(180))
body = db.Column(db.Text, nullable=False)
tags = db.relationship('Tag', secondary=tag_post, backref=db.backref('posts_associated', lazy="dynamic"))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
class Tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20))
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64))
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
posts = db.relationship('Post', backref='author', lazy='dynamic')
How can I query all the posts tagged with a tag, by using the tag.id?
Thanks in advance

The solution is to use a join.
You want all posts that meet a condition. The query should therefore start with the table Post.
Post.query
Then you bind the table posts with your join table using the two columns that should fit. In this case the column post_id in the tag_post table and id in the Post table. Since you use your association table directly, the columns used are referenced using the name via the attribute c.
Post.query\
.join(tag_post, tag_post.c.post_id == Post.id)
Then you filter the second column of your join table based on your condition.
tag_id = 1
Post.query\
.join(tag_post, tag_post.c.post_id == Post.id)\
.filter(tag_post.c.tag_id == tag_id)
Since you want all posts and not just one, close the request with all().
tag_id = 1
tagged_posts = Post.query\
.join(tag_post, tag_post.c.post_id == Post.id)\
.filter(tag_post.c.tag_id == tag_id)\
.all()
The following is a detailed query with the same result, which also includes the third table.
tag_id = 1
tagged_posts = Post.query\
.join(tag_post, tag_post.c.post_id == Post.id)\
.join(Tag, tag_post.c.tag_id == Tag.id)\
.filter(Tag.id == tag_id)\
.all()

Related

How to access Child Table Records on the basis of Parent in Flask

I am developing a Flask app which connects with my phpMyAdmin database using Flask-SQLAlchemy. Now in that database I have 2 tables Posts and Paragraphs such that there are multiple paragraphs associated with one post i.e Paragraphs table has a foreign key PostSNo which is the Primary Key with name SNo in Posts table. The Posts table has a slug column. So what I am trying to achieve is I want to fetch all those Paragraphs records whosePostsno (Foreign Key Column in Paragraphs) == SNo (Primary Key of Posts table)
Here's what I tried but it is raising an exception:
class Posts(db.Model):
'''
Integrating with and Fetching Data from Posts Table
'''
sno = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(30), nullable=False)
date = db.Column(db.String(10), nullable=True)
slug = db.Column(db.String(50), nullable=False)
class Paragraphs(db.Model):
'''
Integrating with and Fetching Data from Paragraphs Table
'''
sno = db.Column(db.Integer, primary_key=True)
paraindex = db.Column(db.Integer, nullable=False)
content = db.Column(db.String(10000), nullable=False)
imageurl = db.Column(db.String(100), nullable=True)
postsno = db.Column(db.Integer, nullable=False)
# App Route to Post Page
#app.route('/post/<string:post_slug>', methods=['GET'])
def post(post_slug):
post = Posts.query.filter_by(slug=post_slug)
para = Paragraphs.query.filter_by(postsno=post.sno)
return render_template('post.html', post=[post, para])
The Exception it is raising is
AttributeError: 'BaseQuery' object has no attribute 'sno'
I think you want this:
post = Posts.query.filter_by(slug=post_slug).all()
para = Paragraphs.query.filter_by(postsno=post.sno).all()
Other operators such as first() or first() are also available but I assume you want all matching records.

How to combine and match 2 tables in Flask

The id is associated with the postID. How can I get the contact information and comment information of that id when I enter the id(/postComments/id)?
I'm getting an internal error...
class posts(db.Model):
__tablename__ = "posts"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100))
postName = db.Column(db.String(100))
postDescription = db.Column(db.String(500))
postLike = db.Column(db.Integer)
class postComment(db.Model):
__tablename__ = "postComment"
id = db.Column(db.Integer, primary_key=True)
postID = db.Column(db.Integer)
senderName = db.Column(db.String(20))
commentPost = db.Column(db.String(300))
#app.route('/postComments/<id>',methods=['GET'])
def get_comment_post(id):
userList = posts.query\
.join(postComment, posts.id == postComment.postID)\
.add_columns(posts.id, posts.name, posts.postDescription, postComment.commentPost, postComment.senderName)\
.filter(posts.id == id)
Modify your models (and perform migrations) to allow reference for foreign keys:
class postComment(db.Model):
__tablename__ = "postComment"
id = db.Column(db.Integer, primary_key=True)
postID = db.Column(db.Integer, db.ForeignKey("posts.id")) # <--- Set to Foreign Key
senderName = db.Column(db.String(20))
commentPost = db.Column(db.String(300))
# Establish relationship
post = db.Relationship("posts", backref="postComment") # <--- backref makes this relationship available in the other class
# This should get simpler...
#app.route('/postComments/<id>',methods=['GET'])
def get_comment_post(id):
# Get the comment based on id
my_comment = postComment.query.get(id)
# Get the post associated with that comment
my_post = my_comment.post
# It looks like you're doing an intricate data transformation.
# Do that here...
# Also, for debugging, consider using the following print statements
print(my_comment.__dict__)
print(my_post.__dict__)
Consider reading this slightly more detailed explanation for establishing relationships.

Can't get Association table to eager load with Movie for particlular User, to show if the User who added the movie is being followed or not Flask

class Movie(db.Model):
__searchable__ = ['genre']
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
username = db.Column(db.String(255))
description = db.Column(db.String(100))
class User(db.Model,UserMixin):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String, nullable=False)
password = db.Column(db.String, nullable=False)
movies = db.relationship('Movie', backref='author', lazy='joined')
followed = db.relationship('User', secondary=followers,
primaryjoin=(followers.c.follower_id==id),
secondaryjoin=(followers.c.followed_id==id),
backref=db.backref('followers', lazy='joined'), lazy='joined')
followers = db.Table('followers',
db.Column('follower_id', db.Integer, db.ForeignKey('user.id'), primary_key = True),
db.Column('followed_id', db.Integer, db.ForeignKey('user.id'), primary_key = True) )
def get_all_movies_follow():
qry = Movie.query.order_by(Movie.id.desc()).all()
movies_schema = MovieSchema(many=True)
return movies_schema.dump(qry)
How to get back an collection (array) where I have a property that shows whether
When I eager_load followers table it's still won't show up in the query.
``` #movi = Movie.query.outerjoin(followers, followers.c.followed_id == Movie.user_id).options(contains_eager( Movie.us).contains_eager( 'followers')).order_by(Movie.id.desc()).all()
Also when I try to use follow unfollow function I get Select statement 'SELECT *
FROM followers, movie AS movie_1
WHERE followers.followed_id = movie_1.user_id' returned no FROM clauses due to auto-correlation; specify correlate(<tables>) to control correlation manually.
which in short is def unfollow(id):
...
current_user.unfollow(user)
db.session.commit()
def follow(self, user):
if not self.is_following(user):
self.followed.append(user)
def unfollow(self, user):
if self.is_following(user):
self.followed.remove(user)
def is_following(self, user):
return self.query.filter(followers.c.followed_id==user.id).count()>0
So I tried adding this to Movie class: ```following = column_property( exists().where(followers.c.followed_id==user_id)) but it has to be also restricted on current_user.id=followers.c.follower_id
I am thinking maybe statement that will be included when I query for the Movie
ok, so this works, granted that I included following in the schema to dump on MovieSchema.
But it has a problem. If there are no matches to (followers.c.follower_id) so user is not following anyone. then I get an empty result and no movie gets loaded at all.
class Movie(db.Model)
...
following = column_property( exists().where(followers.c.followed_id==user_id).correlate_except(followers))
and in query
qry = db.session.query(Movie).filter(followers.c.follower_id == current_user.id).all()

Long loading data in flask-admin relationship dropdown input

How to reduce the amount of data in the related table when editing and inserting item form in Flask-admin.
In my related tables more than 500,000 records ...
And the flask-admin uploads all the data in the associated drop-down input (example film).... and can not do it.
I created my model mysql innodb through SQLAlchemy.
I can load the data in the parts table? or disable the loading of related data
class LfMailerQueue(db.Model):
__tablename__ = 'lf_mailer_queue'
mail_id = db.Column(db.Integer, primary_key=True)
template_id = db.Column(db.ForeignKey(u'lf_mailer.id'))
user_id = db.Column(db.ForeignKey(u'modx_user_attributes.internalKey'), index=True)
film_id = db.Column(db.ForeignKey(u'modx_lfvideo_items.id'), index=True)
email = db.Column(db.String(255))
fullname = db.Column(db.String(255))
status = db.Column(db.Integer, nullable=False, server_default=db.FetchedValue())
film = db.relationship(u'ModxLfvideoItem', primaryjoin='LfMailerQueue.film_id == ModxLfvideoItem.id', backref=u'lf_mailer_queues')
template = db.relationship(u'LfMailer', primaryjoin='LfMailerQueue.template_id == LfMailer.id', backref=u'lf_mailer_queues')
user = db.relationship(u'ModxUserAttribute', primaryjoin='LfMailerQueue.user_id == ModxUserAttribute.internalKey', backref=u'lf_mailer_queues')
class ModxLfvideoItem(db.Model):
__tablename__ = 'modx_lfvideo_items'
id = db.Column(db.Integer, primary_key=True)
group = db.Column(db.String(255), nullable=False, unique=True)
def __str__(self):
return self.group
....
class ModxUserAttribute(db.Model):
....

Selecting an item by its associated tables

I'm working on building a recipe database. I'm trying to build a query wehere I get all recipies that include a certain ingredient (such as onions, carrots), but I'm not how build my query. Essentally I'm trying to get a list of recipies that (given the proper amount of joins) have an Ingredient.name = 'onion'. My models are as follows:
ingredients = db.Table('ingredients',
db.Column('modified_ingredient', db.Integer, db.ForeignKey('modified_ingredient.id')),
db.Column('ingredient', db.Integer, db.ForeignKey('ingredient.id'))
)
modifiers = db.Table('modifiers',
db.Column('modified_ingredient', db.Integer, db.ForeignKey('modified_ingredient.id')),
db.Column('modifier', db.Integer, db.ForeignKey('modifier.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)
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))
ingredients = db.relationship('Ingredient', secondary=ingredients,
backref=db.backref('ingredients', lazy='dynamic'), lazy='dynamic')
modifiers = db.relationship('Modifier', secondary=modifiers,
backref=db.backref('modifiers', lazy='dynamic'), lazy='dynamic')
It's mostly my inexperience with SQL and SQLAlchemy that is stumping me. I know that I'm joining something, but I'm not exactly sure how to phrase it in a way that works.
Option-1: very tidy, but might not be the most efficient due to nested EXISTS clause:
q = (db.session.query(Recipe)
.filter(Recipe.ingredients.any(
ModifiedIngredient.ingredients.any(
Ingredient.name == 'onion')
)))
Option-2: should be faster, but if you query only certain columns (use query(Recipe.name, ..) instead of whole objects as below), you will end with with multiple results per each Recipe row because of JOINs:
q = (db.session.query(Recipe)
.join(Recipe.ingredients)
.join(Ingredient, ModifiedIngredient.ingredients)
.filter(Ingredient.name == 'onion')
)