Given this Many to Many relationship:
tagmap = db.Table('tagmap', db.Model.metadata,
db.Column('post_id', db.Integer, db.ForeignKey('posts.id'),
db.Column('tag_id', db.Integer, db.ForeignKey('tags.id'),)
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.Text)
tags = db.relationship('Tag', secondary=tagmap, backref='posts')
class Tag(db.Model):
__tablename__ = 'tags'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
How would you construct a query to select all the Posts including an iterable list of associated Tag objects?
Desired query result structure:
[ <Post 1, "Blah", [<Tag "Goober">, <Tag "Mexico">]>,
<Post 2, "Daft Punk", [<Tag "French">]>,
<Post 3, "Slurpee", [<Tag "Diabetes">, <Tag "Tasty">, <Tag "Sugary">]>,
<Post 4, "Lasers", []> ]
Example desired result usage in a Jinja template:
{% for post in posts %}
{{ post.text }}
<hr>
{% for tag in post.tags %}
{{ tag.name }},
{% endfor %}
{% endfor %}
Edit:
When trying to extend the query, the tags stop being included. See failed query below:
Post.query.join(User)\
.options(db.joinedload(Post.tags))\
.order_by(Post.create_date.desc()).limit(5)\
.values(User.name.label('author'),
Post.create_date,
Post.text,)
Edit 2:
So this query works great:
posts = db.session.query(Post, User.name.label('author')).join(User)\
.options(db.joinedload(Post.tags))\
.order_by(Post.created.desc()).limit(5)
but if I want to be selective about the columns in Post I select:
# I only need the post text, nothing else
posts = db.session.query(Post.text, User.name.label('author')).join(User)\
.options(db.joinedload(Post.tags))\
.order_by(Post.created.desc()).limit(5)
It begins to fall apart quickly:
ArgumentError: Query has only expression-based entities - can't find property named 'tags'.
So I try to add the tags back:
# Yes, I know this is wrong
posts = db.session.query(Post.text, Post.tags, User.name.label('author'))\
.join(User)\
.options(db.joinedload(Post.tags))\
.order_by(Post.created.desc()).limit(5)
Which still fails.
So if you only want specific columns from the parent object (Post), where to you say in the expression, "I still want the tags"?
It's not really that important now, but I figured it would be useful to know. Or even know that it's not possible.
How do you create a many to many relationship in SQLAlchemy? Python Flask and SQLAlchemy ORM Many to Many relationship between two tables is achieved by adding an association table such that it has two foreign keys – one from each table's primary key.
As the documentation says, all() returns the result of the query as a list.
The sqlalchemy backref is one of the type keywords and it passed as the separate argument parameters which has to be used in the ORM mapping objects. It mainly includes the event listener on the configuration attributes with both directions of the user datas through explicitly handling the database relationships.
joined loading - available via lazy='joined' or the joinedload() option, this form of loading applies a JOIN to the given SELECT statement so that related rows are loaded in the same result set.
I assume that you want to use eager loading for relationships. You can achieve that either by setting lazy
keyword argument in your relationship to 'joined'
or 'subquery'
:
class Post(db.Model):
# ...
tags = db.relationship('Tag', secondary=tagmap, backref='posts',
lazy='joined') # Or lazy='subquery'
Or you can set it per query:
q = Post.query.options(db.joinedload(Post.tags)).all()
# Or db.subqueryload(Post.tags)
Refer to Eager Loading part of ORM tutorial and Relationship Loading Techniques for more details.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With