问题
So i am trying to add to add a like button feature to my code that allows users to like specific posts. The likes will be linked to a logged in user and the number of likes will be shown. Implementing the front end wont be difficult but i am having a problem with the back end.
I am using this post here as a guide which does a follower system instead
This is what I have so far?
I have created a table for likes in models.py :
likers = db.Table('likers',
db.Column('liker_id', db.Integer, db.ForeignKey('post.id')),
db.Column('liked_id', db.Integer, db.ForeignKey('post.id'))
)
In my Models.py for my user class:
class User(db.Model, UserMixin):
#Code
liked = db.relationship(
'User', secondary=likers,
primaryjoin=(likers.c.liker_id == id),
secondaryjoin=(likers.c.liked_id == id),
backref = db.backref('likers', lazy='dynamic'), lazy='dynamic')
def like(self, post):
if not self.is_liking(post):
self.liked.append(post)
def unlike(self, post):
if self.is_liking(post):
self.liked.remove(post)
def is_liking(self, post):
return self.liked.filter(
likers.c.liked_id == post.id).count() > 0
in my routes.py- for my users blueprint I have:
@users.route("/like/<int:post_id>")
@login_required
def like(post_id):
post = Post.query.get_or_404(post_id)
current_user.like(post)
db.session.commit()
flash('Post has been liked')
return redirect(url_for('posts.post', post_id=post.id))
@users.route("/unlike/<int:post_id>")
@login_required
def unlike(post_id):
post = Post.query.get_or_404(post_id)
current_user.unlike(post)
db.session.commit()
flash('Post has been unliked')
return redirect(url_for('posts.post', post_id=post.id))
What am i doing wrong? I keep getting errors such as :
builtins.KeyError
KeyError: 'likers'
I have done a comment section annd i know the relatioship for the likes will be simialr to the comments but i am struggling to implement it. I am relatively new to flask and i have tried using the documentations but haven't found anything to help me...
This is my final hope.
回答1:
class User(UserMixin, db.Model):
# Code
liked = db.relationship(
'PostLike',
foreign_keys='PostLike.user_id',
backref='user', lazy='dynamic')
def like_post(self, post):
if not self.has_liked_post(post):
like = PostLike(user_id=self.id, post_id=post.id)
db.session.add(like)
def unlike_post(self, post):
if self.has_liked_post(post):
PostLike.query.filter_by(
user_id=self.id,
post_id=post.id).delete()
def has_liked_post(self, post):
return PostLike.query.filter(
PostLike.user_id == self.id,
PostLike.post_id == post.id).count() > 0
class PostLike(db.Model):
__tablename__ = 'post_like'
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'))
@app.route('/like/<int:post_id>/<action>')
@login_required
def like_action(post_id, action):
post = Post.query.filter_by(id=post_id).first_or_404()
if action == 'like':
current_user.like_post(post)
db.session.commit()
if action == 'unlike':
current_user.unlike_post(post)
db.session.commit()
return redirect(request.referrer)
Then when you're listing your posts, set your anchors something like this:
{% for post in posts %}
{% if current_user.has_liked_post(post) %}
<a href="{{ url_for('like_action', post_id=post.id, action='unlike') }}">Unlike</a>
{% else %}
<a href="{{ url_for('like_action', post_id=post.id, action='like') }}">Like</a>
{% endif %}
{% endfor %}
Let's assume your Post
model looks something like this:
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text)
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
recipient_id = db.Column(db.Integer, db.ForeignKey('user.id'))
likes = db.relationship('PostLike', backref='post', lazy='dynamic')
You'd use:
p = Post.query.filter_by(id=1).first()
p.likes.count()
Or, you'd use this in your .html file:
{% for post in posts %}
{% if current_user.has_liked_post(post) %}
<a href="{{ url_for('like_action', post_id=post.id, action='unlike') }}">Unlike</a>
{% else %}
<a href="{{ url_for('like_action', post_id=post.id, action='like') }}">Like</a>
{% endif %}
{{ post.likes.count() }} likes
{% endfor %}
回答2:
Your error indicates that the user instance does not have a likers
relationship properly defined.
I suspect there are several errors here:
- Your
likers
table should have the foreign key forliker_id
pointing at the users table, not posts. Your
liked
relationship in theUser
model should be a relationship with thePost
model (with thelikers
table as secondary) and not be in a relationship with itself (ie theUser
model). Try this for your relationship:liked = db.relationship( 'Post', secondary="likers", primaryjoin="likers.liker_id == users.id", secondaryjoin="likers.liked_id == posts.id", backref=db.backref('likers', lazy='dynamic'), lazy='dynamic')
Your
is_liking()
method seems strange. I would write it as either:(if not expecting many liked posts per user)
return post in self.liked
(if expecting many liked posts per user)
return db.session.execute("SELECT COUNT(*) FROM likers WHERE liked_id = :post_id AND liker_id = :user_id", {'user_id': self.id, 'post_id': post.id}).fetchone()[0] > 0
(not related but possibly incorrect) The order that your User model inherits from
db.Model
andUserMixin
matters, currently your UserMixin is only used for methods which are not found in db.Model and will not override anything (maybe that's what you want). Ref: Python's Method Resolution Order (MRO)
回答3:
Above code works but logout of user maker error because of the "current_user" . So our html file must be like this
{% for post in posts %}
{% if current_user.is_authenticated %}
{% if current_user.has_liked_post(post) %}
<a href="{{ url_for('like_action', post_id=post.id, action='unlike') }}">Unlike</a>
{% else %}
<a href="{{ url_for('like_action', post_id=post.id, action='like') }}">Like</a>
{% endif %}
{{ post.likes.count() }} likes
{% else %}
{{ post.likes.count() }} likes
{% endif %}{% endfor %}
来源:https://stackoverflow.com/questions/52665707/how-do-i-implement-a-like-button-function-to-posts-in-python-flask