I am developing a social flask web application using Miguel Gringberg's book 'Flask Web Development'. I am having trouble implementing the like feature for posts and comments. I tried replicating the follow feature in the book but I get a sqlalchemy error saying that "a relationship cannot be found for the Post.liked column in the Post model" (I believe there are more errors) here is my models.py code.
# Set website access permissions
class Permission:
FOLLOW = 0x01
COMMENT = 0x02
WRITE_ARTICLES = 0x04
MODERATE_COMMENTS = 0x08
ADMINISTER = 0x80
# User roles
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
default = db.Column(db.Boolean, default=False, index=True)
permissions = db.Column(db.Integer)
users = db.relationship('User', backref='role', lazy='dynamic')
# Crete roles in the db
@staticmethod
def insert_roles():
roles = {
'User': (Permission.FOLLOW |
Permission.COMMENT |
Permission.WRITE_ARTICLES, True),
'Moderator': (Permission.FOLLOW |
Permission.COMMENT |
Permission.WRITE_ARTICLES |
Permission.MODERATE_COMMENTS, False),
'Administrator': (0xff, False)
}
for r in roles:
role = Role.query.filter_by(name=r).first()
if role is None:
role = Role(name=r)
role.permissions = roles[r][0]
role.default = roles[r][1]
db.session.add(role)
db.session.commit()
def __repr__(self):
return '<Role %r>' % self.name
# Follows association table
class Follow(db.Model):
__tablename__ = 'follows'
follower_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)
followed_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
class Like(db.Model):
__tablename__ = 'likes'
post_liked_id = db.Column(db.Integer, db.ForeignKey('posts.id'), primary_key=True)
comment_liked_id =db.Column(db.Integer, db.ForeignKey('comments.id'), primary_key=True)
liker_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
# Users table
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key= True, autoincrement=True)
first_name = db.Column(db.String(64))
last_name = db.Column(db.String(64))
dob = db.Column(db.Date)
gender = db.Column(db.String(64))
nationality = db.Column(db.String(64))
residence = db.Column(db.String(64))
postal_code = db.Column(db.String(64))
username = db.Column(db.String(64), unique=True)
email = db.Column(db.String(64), unique=True)
password_hash = db.Column(db.String(128))
member_since = db.Column(db.DateTime(), default=datetime.utcnow)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
confirmed = db.Column(db.Boolean, default=False)
location = db.Column(db.String(64))
about_me = db.Column(db.Text())
last_seen = db.Column(db.DateTime(), default=datetime.utcnow)
bio = db.Column(db.String(100))
profile_pic = db.Column(db.String, default=None)
profile_pic_url = db.Column(db.String, default=None)
posts = db.relationship('Post', backref='author', lazy='dynamic')
followed = db.relationship('Follow', foreign_keys=[Follow.follower_id], backref=db.backref('follower', lazy='joined'),
lazy='dynamic', cascade='all, delete-orphan')
followers = db.relationship('Follow', foreign_keys=[Follow.followed_id], backref=db.backref('followed', lazy='joined'),
lazy='dynamic', cascade='all, delete-orphan')
comments = db.relationship('Comment', backref='author', lazy='dynamic')
liker = db.relationship('Like', backref='liker', lazy='dynamic')
# Define default role for users
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
if self.role is None:
if self.email == 'hardingalex@live.com':
self.role = Role.query.filter_by(permissions=0xff).first()
if self.role is None:
self.role = Role.query.filter_by(default=True).first()
self.follow(self)
# User account confirmation
def generate_confirmation_token(self, expiration=86400):
s = Serializer(current_app.config['SECRET_KEY'], expiration)
return s.dumps({'confirm': self.id})
def confirm(self, token):
s = Serializer(current_app.config['SECRET_KEY'])
try:
data = s.loads(token)
except:
return False
if data.get('confirm') != self.id:
return False
self.confirmed = True
db.session.add(self)
return True
# Password hashing and verification
@property
def password(self):
raise AttributeError('Password is not a readable attribute')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
# Load and remember logged in user
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
# Evaluate whether a user has a given permission
def can(self, permissions):
return self.role is not None and (self.role.permissions & permissions) == permissions
def is_administrator(self):
return self.can(Permission.ADMINISTER)
#Refresh users last visit
def ping(self):
self.last_seen = datetime.utcnow()
db.session.add(self)
#Connection helper methods
def follow(self, user):
if not self.is_following(user):
f = Follow(follower=self, followed=user)
db.session.add(f)
def unfollow(self, user):
f = self.followed.filter_by(followed_id=user.id).first()
if f:
db.session.delete(f)
def is_following(self, user):
return self.followed.filter_by(followed_id=user.id).first() is not None
def is_followed_by(self, user):
return self.followers.filter_by(follower_id=user.id).first() is not None
#get followed posts
@property
def followed_posts(self):
return Post.query.join(Follow, Follow.followed_id == Post.author_id).filter(Follow.follower_id == self.id)
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text())
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
comments = db.relationship('Comment', backref='post', lazy='dynamic')
liked = db.relationship('Like', foreign_keys=[Like.liker_id], backref=db.backref('liker', lazy='joined'),
lazy='dynamic', cascade='all, delete-orphan')
likers = db.relationship('Like', foreign_keys=[Like.post_liked_id], backref=db.backref('liked', lazy='joined'),
lazy='dynamic', cascade='all, delete-orphan')
# Post Likes helper methods
def like_post(self, post):
if not self.is_liking_post(post):
l = Like(liker=self, liked=post)
db.session.add(l)
def unlike_post(self, post):
l = self.liked.filter_by(post_liked_id=post.id).first()
if l:
db.session.delete(l)
def is_liking_post(self, post):
return self.liked.filter_by(post_liked_id=post.id).first() is not None
def is_liked_by(self, post):
return self.likers.filter_by(liker_id=post.id).first() is not None
#comments model
class Comment(db.Model):
__tablename__ = 'comments'
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text)
timestamp = db.Column(db.DateTime,index=True, default=datetime.utcnow)
disabled = db.Column(db.Boolean)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
post_id = db.Column(db.Integer, db.ForeignKey('posts.id'))
liked = db.relationship('Like', foreign_keys=[Like.liker_id], backref=db.backref('liker', lazy='joined'),
lazy='dynamic', cascade='all, delete-orphan')
likers = db.relationship('Like', foreign_keys=[Like.comment_liked_id], backref=db.backref('liked', lazy='joined'),
lazy='dynamic', cascade='all, delete-orphan')
#Comment Likes helper methods
def like_comment(self, comment):
if not self.is_liking_comment(comment):
c = Like(liker=self, liked=comment)
db.session.add(c)
def unlike_comment(self, comment):
c = self.liked.filter_by(comment_liked_id=comment.id).first()
if c:
db.session.delete(c)
def is_liking_comment(self, comment):
return self.liked.filter_by(comment_liked_id=comment.id).first() is not None
def is_liked_by(self, comment):
return self.likers.filter_by(liker_id=comment.id).first() is not None
First of all, you are mixing likes for posts and for comments. That is not going to go well. I recommend that you separate post likes from comment likes using two separate many-to-many relationships.
The error that you are getting occurs because you are not implementing the many-to-many relationship correctly. For example, to represent post likes, you have a many-to-many relationship between users and posts. The association table could be called PostLikes
for example, and it would have two foreign keys, user_id
and post_id
(maybe also a timestamp if you want to record the time of the like).
On the user side, you can define the relationship more or less like this:
class User:
post_likes = db.relationship('PostLikes', backref=db.backref('user', lazy='joined'),
lazy='dynamic', cascade='all, delete-orphan')
And on the post side, it would be:
class Post:
user_likes = db.relationship('PostLikes', backref=db.backref('post', lazy='joined'),
lazy='dynamic', cascade='all, delete-orphan')
So then, given a user, you can say user.post_likes
and that will give you a db query that returns all the posts the user liked. And given a post, you can say post.user_likes
to get all the users that liked that post.
You will then repeat all the above for comment likes.
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.