I'm so sorry, there are a lot of question about this issue, but I just can't do it. I've been trying for literally two days.
I have three models:
class Post(db.Model):
### other columns
oylar = db.relationship('Vote',backref='post_oylar',lazy='dynamic', cascade="all, delete")
class Plan(db.Model):
### other columns
oylar = db.relationship('Vote',backref='oylar',lazy='dynamic', cascade="all, delete")
class Vote(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
post_id = db.Column(db.Integer, db.ForeignKey("post.id"))
plani_id = db.Column(db.Integer, db.ForeignKey("plan.id"))
I need to query Plan and Post based on Vote user_id.
Let's say I have this A user with Id 1. Basically, I want to see which Plan and Post this user voted for.
I've tried so many queries that I can't even think of which one I should write here. But in order to be more clear, let me show you what I mean with this example:
post = db.session.query(Post).join(Vote).filter(Vote.user_id == 1)
plan = db.session.query(Plan).join(Vote).filter(Vote.user_id == 1)
final_query = #there should be something to merge these two queries.
It's been a nightmare for me. I don't know SQL syntax and I started learning with SQL-Alchemy, so please don't shoot.
Thanks for your time in advance.
Update : Solution that fits me best is using single-table inheritance. It has made my life easier.
You use a many-to-many relationship with an association object .
I think the models are okay, but I've restructured them a bit.
class Vote(db.Model):
__tablename__ = 'votes'
id = db.Column(db.Integer, primary_key=True)
# Foreign Keys
plan_id = db.Column(db.Integer, db.ForeignKey('plans.id'))
post_id = db.Column(db.Integer, db.ForeignKey('posts.id'))
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
# Relationships
# If a user, post or plan is deleted, referencing votes are also removed.
# The associated plans and posts are loaded with the vote using a JOIN statement.
plan = db.relationship('Plan',
backref=db.backref('votes', cascade='all, delete-orphan'),
lazy='joined')
post = db.relationship('Post',
backref=db.backref('votes', cascade='all, delete-orphan'),
lazy='joined')
user = db.relationship('User',
backref=db.backref('votes', cascade='all, delete-orphan'))
def __repr__(self):
return f'Vote(plan_id={self.plan_id}, post_id={self.post_id}, user_id={self.user_id})'
class Plan(db.Model):
__tablename__ = 'plans'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), index=True, nullable=False)
def __repr__(self):
return f'Plan(name={self.name})'
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(255), index=True, nullable=False)
def __repr__(self):
return f'Post(title={self.title})'
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), index=True, nullable=False)
# All plans and posts that have been voted for can be reached via jointable.
# CAUTION, objects can be added to the lists, but because of the viewonly flag
# they are not transferred to the database during a commit.
# An inconsistent state is therefore possible.
#
# _voted_plans = db.relationship(
# 'Plan',
# secondary='votes',
# backref=db.backref('users_voted', viewonly=True),
# viewonly=True
# )
#
# _voted_posts = db.relationship(
# 'Post',
# secondary='votes',
# backref=db.backref('users_voted', viewonly=True),
# viewonly=True
# )
def __repr__(self):
return f'User(name={self.name})'
On the one hand, you can use the ORM method using the virtual relationships to list all the associated models. In this case, your table votes acts as a jointable and the associated objects of the classes Post and Plan can be queried directly via the relationship.
plan_post_pairs = [(vote.plan, vote.post) for vote in Vote.query.filter_by(user_id=user_id).all()]
As an alternative, you can also write your own request. As an example I'll give you both a SQL SELECT statement and a JOIN statement. I also ask for the identifier of the vote to list duplicate votes by a user on the same plan-post combinations.
# SELECT stmt
items = db.session.query( # SELECT ... FROM ...
Vote.id, Plan, Post
).filter( # WHERE ...
Vote.plan_id == Plan.id, # ... AND
Vote.post_id == Post.id, # ... AND
Vote.user_id == user_id # ...
).all()
# JOIN stmt
items = db.session.query(Vote.id, Plan, Post)\ # SELECT ...
.select_from(Vote)\ # FROM ...
.outerjoin(Plan, Post)\ # LEFT OUTER JOIN ... ON ...
.filter(Vote.user_id == user_id)\ # WHERE ...
.all()
The following example is a little more advanced and may help you in the future. All plan-post combinations of a user are requested including the number of votes for the respective pair from this user.
subquery = db.session\
.query(Vote.plan_id, Vote.post_id, db.func.count('*').label('count'))\
.group_by(Vote.plan_id, Vote.post_id)\
.filter(Vote.user_id == user_id)\
.subquery()
items = db.session\
.query(Plan, Post, subquery.c.count)\
.select_from(subquery)\
.outerjoin(Plan, subquery.c.plan_id == Plan.id)\
.outerjoin(Post, subquery.c.post_id == Post.id)\
.all()
Because you're creating relationships to your Vote
model, I would suggest you also establish a relationship between Vote
and User
:
# Modify your class to include the following line
class Vote(db.Model):
...
user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
user = db.Relationship("User", backref="vote") # <--- This assumes you have a `User` model
...
Now that that relationship is established, I would access these child objects from the user-vote
relationship:
# 1. Get your user
my_user_id = 1
my_user = db.session.query(Post).get(my_user_id)
# Get the vote for this user --> this will return a list
# Note: there may be many votes, I'll use the first index as a demonstrative example.
my_votes = my_user.vote
# If there aren't any votes
if not my_votes:
raise Exception(f'User id={my_user.id} has no votes')
# 2. Get the first vote
my_vote = my_votes[0]
# 3. Get the children from that vote
# These are the keywords specified in the 'backref' parameter
my_post = my_vote.post_oylar
my_plan = my_vote.oylar
# Finally, print them
print('post:', my_post.__dict__)
print('plan:', my_plan.__dict__)
As a side note, I'd suggest you do some additional research on how to best establish relationships in your ORM models – it looks like that might be the source of confusion.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.