flask中models设计

1. 自关联
class Comment(db.Model):
    __tablename__ = 'albumy_comment'
    id = db.Column(db.Integer, primary_key=True)
    body = db.Column(db.Text)
    timestamp = db.Column(db.DateTime, default=datetime.utcnow, index=True)
    flag = db.Column(db.Integer, default=0)

    replied_id = db.Column(db.Integer, db.ForeignKey('albumy_comment.id'))
    user_id = db.Column(db.Integer, db.ForeignKey('albumy_user.id'))
    photo_id = db.Column(db.Integer, db.ForeignKey('albumy_photo.id'))

    photo = db.relationship('Photo', back_populates='comments')
    user = db.relationship('User', back_populates='comments')
    replies = db.relationship('Comment', back_populates='replied', cascade='all')  # 一  我下面所有给我的评论
    replied = db.relationship('Comment', back_populates='replies', remote_side=[id])  # 多 我对哪条评论进行的评论

以评论表为例,评论下又可以有针对该评论的回复,因此在表中增加 replied_id 外键字段,指向该表的主键id。

在设置关系属性时,需要再多的一方设置remote_side=[id]。

2. 第三张表中的多个外键字段执行同一个表中的同一个字段
class Follow(db.Model):
    __tablename__ = 'albumy_follow'
    follower_id = db.Column(db.Integer, db.ForeignKey('albumy_user.id'), primary_key=True)
    followed_id = db.Column(db.Integer, db.ForeignKey('albumy_user.id'), primary_key=True)
    timestamp = db.Column(db.DateTime, default=datetime.utcnow)

    follower = db.relationship('User', foreign_keys=[follower_id], back_populates='following', lazy='joined')
    followed = db.relationship('User', foreign_keys=[followed_id], back_populates='followers', lazy='joined')


class User(UserMixin, db.Model):
    __tablename__ = 'albumy_user'
    id = db.Column(db.INTEGER, primary_key=True)
    # 资料
    username = db.Column(db.String(20), unique=True, index=True)
    email = db.Column(db.String(254), unique=True, index=True)
    password_hash = db.Column(db.String(128))
    name = db.Column(db.String(30))
    website = db.Column(db.String(255))
    bio = db.Column(db.String(120))
    location = db.Column(db.String(50))
    member_since = db.Column(db.DateTime, default=datetime.utcnow)
    avatar_s = db.Column(db.String(64))
    avatar_m = db.Column(db.String(64))
    avatar_l = db.Column(db.String(64))
    avatar_raw = db.Column(db.String(64))
    receive_comment_notification = db.Column(db.Boolean, default=True)
    receive_follow_notification = db.Column(db.Boolean, default=True)
    receive_collect_notification = db.Column(db.Boolean, default=True)
    show_collections = db.Column(db.Boolean, default=True)
    role_id = db.Column(db.Integer, db.ForeignKey('albumy_role.id'))
    role = db.relationship('Role', back_populates='users')
    photos = db.relationship('Photo', back_populates='user', cascade='all')
    collections = db.relationship('Collect', back_populates='collector', cascade='all')  # 如:都收藏了那些图片
    comments = db.relationship('Comment', back_populates='user', cascade='all')
    following = db.relationship('Follow', foreign_keys=[Follow.follower_id], back_populates='follower',
                                lazy='dynamic', cascade='all')  # 都关注了哪些用户
    followers = db.relationship('Follow', foreign_keys=[Follow.followed_id], back_populates='followed',
                                lazy='dynamic', cascade='all')  # 都被哪些用户
    notifications = db.relationship('Notification', back_populates='receiver', cascade='all')
    # 用户状态
    confirmed = db.Column(db.Boolean, default=False)
    locked = db.Column(db.Boolean, default=False)
    active = db.Column(db.Boolean, default=True)

以 Follow表(关注表)与user表为例,follow表中记录着关注者id 与被关注着id,这两个外键字段都指向user表中的id。

因为在Follow模型中,两个字段定义的外键是指向同一个表的同一个字段(user.id)的。而当我们需要在Follow模型上建立反向属性时,SQLAlchemy没法知道哪个外键对应哪个反向属性,所以我们需要在关系函数中使用foreign_keys参数来明确对应的字段。
Follow表:

follower = db.relationship('User', foreign_keys=[follower_id], back_populates='following', lazy='joined')
followed = db.relationship('User', foreign_keys=[followed_id], back_populates='followers', lazy='joined')

User表:

following = db.relationship('Follow', foreign_keys=[Follow.follower_id], back_populates='follower',
                                lazy='dynamic', cascade='all')  # 都关注了哪些用户
followers = db.relationship('Follow', foreign_keys=[Follow.followed_id], back_populates='followed',
                                lazy='dynamic', cascade='all')  # 都被哪些用户
3. 使用关联表表示多对多关系
class Role(db.Model):
    __tablename__ = 'albumy_role'
    id = db.Column(db.Integer, primary_key=True)
    code = db.Column(db.String(21), unique=True)
    name = db.Column(db.String(21), unique=True)
    desc = db.Column(db.String(64), nullable=True)
    users = db.relationship('User', back_populates='role')
    permissions = db.relationship('Permission', secondary='albumy_roles_permissions', back_populates='roles')


class Permission(db.Model):
    __tablename__ = 'albumy_permission'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(21), unique=True)
    desc = db.Column(db.String(64), nullable=True)
    roles = db.relationship('Role', secondary='albumy_roles_permissions', back_populates='permissions')


roles_permissions = db.Table(
    'albumy_roles_permissions',
    db.Column('id', db.Integer, primary_key=True),
    db.Column('role_id', db.Integer, db.ForeignKey('albumy_role.id')),
    db.Column('permission_id', db.Integer, db.ForeignKey('albumy_permission.id'))
)

1. 使用关联表很方便,唯一的缺点是只能用来表示关系,不能用来存储数据。
2. 当使用关联表时,SQLAlchemy会帮助我们操作关系,所以对关系某一侧调用关系属性会直接返回关系另一侧的对应记录。但是使用关联模型时,我们则需要手动操作关系。

4. 使用关联模型表示多对多关系
class Photo(db.Model):
    __tablename__ = 'albumy_photo'
    id = db.Column(db.Integer, primary_key=True)
    description = db.Column(db.String(500))
    filename = db.Column(db.String(64))
    filename_s = db.Column(db.String(64))
    filename_m = db.Column(db.String(64))
    flag = db.Column(db.Integer, default=0)  # 举报次数
    can_comment = db.Column(db.Boolean, default=True)
    timestamp = db.Column(db.DateTime, default=datetime.utcnow)
    user_id = db.Column(db.Integer, db.ForeignKey('albumy_user.id'))
    user = db.relationship(User, back_populates='photos')
    tags = db.relationship('Tag', back_populates='photos', secondary='albumy_photos_tags', cascade='all')

    collectors = db.relationship('Collect', back_populates='collected', cascade='all')  # 如:被收藏的数量
    comments = db.relationship('Comment', back_populates='photo', cascade='all')
# 关联模型
class Collect(db.Model):
    __tablename__ = 'albumy_collect'
    # id = db.Column(db.Integer, primary_key=True)
    collector_id = db.Column(db.Integer, db.ForeignKey('albumy_user.id'), primary_key=True)  # 收藏者id
    collected_id = db.Column(db.Integer, db.ForeignKey('albumy_photo.id'), primary_key=True)  # 被收藏图片id
    timestamp = db.Column(db.DateTime, default=datetime.utcnow)
    collector = db.relationship('User', back_populates='collections', lazy='joined')
    collected = db.relationship('Photo', back_populates='collectors', lazy='joined')
    # __table_args__ = (
    #     db.UniqueConstraint('collector_id', 'collected_id', name='u_collector_id_collected_id'),
    #     # db.Index('ix_user_post_user_id_insert_time', 'user_id', 'insert_time'),
    # )

当使用关联表时,SQLAlchemy会帮助我们操作关系,所以对关系某一侧调用关系属性会直接返回关系另一侧的对应记录。但是使用关联模型时,我们则需要手动操作关系。具体的表现是,我们在Photo和User模型中定义的关系属性返回的不再是关系另一侧的记录,而是存储对应关系的中间人——Collect记录。在Collect记录中添加的标量关系属性collector和collected,分别表示收藏者和被收藏图片,指向对应的User和Photo记录,我们需要进一步调用这两个关系属性,才可以获取关系另一侧的记录。

from flask_login import UserMixin
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash
from flask_avatars import Identicon
from flask import current_app
import os

from albumy.extensions import db


class Follow(db.Model):
    __tablename__ = 'albumy_follow'
    follower_id = db.Column(db.Integer, db.ForeignKey('albumy_user.id'), primary_key=True)
    followed_id = db.Column(db.Integer, db.ForeignKey('albumy_user.id'), primary_key=True)
    timestamp = db.Column(db.DateTime, default=datetime.utcnow)

    follower = db.relationship('User', foreign_keys=[follower_id], back_populates='following', lazy='joined')
    followed = db.relationship('User', foreign_keys=[followed_id], back_populates='followers', lazy='joined')


class User(UserMixin, db.Model):
    __tablename__ = 'albumy_user'
    id = db.Column(db.INTEGER, primary_key=True)
    # 资料
    username = db.Column(db.String(20), unique=True, index=True)
    email = db.Column(db.String(254), unique=True, index=True)
    password_hash = db.Column(db.String(128))
    name = db.Column(db.String(30))
    website = db.Column(db.String(255))
    bio = db.Column(db.String(120))
    location = db.Column(db.String(50))
    member_since = db.Column(db.DateTime, default=datetime.utcnow)
    avatar_s = db.Column(db.String(64))
    avatar_m = db.Column(db.String(64))
    avatar_l = db.Column(db.String(64))
    avatar_raw = db.Column(db.String(64))
    receive_comment_notification = db.Column(db.Boolean, default=True)
    receive_follow_notification = db.Column(db.Boolean, default=True)
    receive_collect_notification = db.Column(db.Boolean, default=True)
    show_collections = db.Column(db.Boolean, default=True)
    role_id = db.Column(db.Integer, db.ForeignKey('albumy_role.id'))
    role = db.relationship('Role', back_populates='users')
    photos = db.relationship('Photo', back_populates='user', cascade='all')
    collections = db.relationship('Collect', back_populates='collector', cascade='all')  # 如:都收藏了那些图片
    comments = db.relationship('Comment', back_populates='user', cascade='all')
    following = db.relationship('Follow', foreign_keys=[Follow.follower_id], back_populates='follower',
                                lazy='dynamic', cascade='all')  # 都关注了哪些用户
    followers = db.relationship('Follow', foreign_keys=[Follow.followed_id], back_populates='followed',
                                lazy='dynamic', cascade='all')  # 都被哪些用户
    notifications = db.relationship('Notification', back_populates='receiver', cascade='all')
    # 用户状态
    confirmed = db.Column(db.Boolean, default=False)
    locked = db.Column(db.Boolean, default=False)
    active = db.Column(db.Boolean, default=True)

    def __init__(self, **kwargs):
        super(User, self).__init__(**kwargs)
        self.generate_avatar()
        self.set_role()
        self.follow(self)

    def generate_avatar(self):
        """生成用户头像"""
        avatar = Identicon()
        filenames = avatar.generate(text=self.username)
        self.avatar_s = filenames[0]
        self.avatar_m = filenames[1]
        self.avatar_l = filenames[2]
        db.session.commit()

    def set_role(self):
        """为用户设置角色,默认为user"""
        if self.role is None:
            role = Role.query.filter_by(code='user').first()
            self.role = role
            db.session.commit()

    def set_password(self, pwd):
        """设置加密密码"""
        self.password_hash = generate_password_hash(pwd)

    def check_password(self, pwd):
        """检验密码正确性"""
        return check_password_hash(self.password_hash, pwd)

    def is_admin(self):
        """判断用户是否具有管理员的角色"""
        return self.role.code == 'administrator'

    def can(self, permission_name):
        """判断用户是否具有某个权限"""
        permission = Permission.query.filter_by(name=permission_name).first()
        return permission is not None and self.role is not None and permission in self.role.permissions

    def collect(self, photo):
        """
        收藏图片
        :param photo: 图片对象
        :return:
        """
        if not self.is_collecting(photo):
            collect = Collect(collector=self, collected=photo)
            db.session.add(collect)
            db.session.commit()

    def uncollect(self, photo):
        """
        取消图片收藏
        :param photo: 图片对象
        :return:
        """
        collect = Collect.query.with_parent(self).filter_by(collected_id=photo.id).first()
        if collect:
            db.session.delete(collect)
            db.session.commit()

    def is_collecting(self, photo):
        """
        是否收藏图片
        :return: 图片对象
        """
        collect = Collect.query.with_parent(self).filter_by(collected_id=photo.id).first()
        if collect:
            return True
        else:
            return False

    def follow(self, user):
        """
        关注用户
        :param user: user对象
        :return:
        """
        if not self.is_following(user):
            follow = Follow(follower=self, followed=user)
            db.session.add(follow)
            db.session.commit()

    def unfollow(self, user):
        """
        取消关注
        :param user: user对象
        :return:
        """
        follow = self.following.filter_by(followed_id=user.id).first()
        if follow:
            db.session.delete(follow)
            db.session.commit()

    def is_following(self, user):
        """
        是否关注用户
        :param user: user对象
        :return:
        """
        if user.id is None:
            return False
        return self.following.filter_by(followed_id=user.id).first() is not None

    def is_followed_by(self, user):
        """
        用户是否被关注
        :param user: user对象
        :return:
        """
        return self.followers.filter_by(follower_id=user.id).first() is not None

    def lock(self):
        self.locked = True
        self.role = Role.query.filter_by(name='Locked').first()
        db.session.commit()

    def unlock(self):
        self.locked = False
        self.role = Role.query.filter_by(name='User').first()
        db.session.commit()

    @property
    def is_active(self):
        return self.active

    def block(self):
        self.active = False
        db.session.commit()

    def unblock(self):
        self.active = True
        db.session.commit()


@db.event.listens_for(User, 'after_delete', named=True)
def delete_avatars(**kwargs):
    target = kwargs['target']
    for filename in [target.avatar_s, target.avatar_m, target.avatar_l, target.avatar_raw]:
        if filename is not None:  # avatar_raw may be None
            path = os.path.join(current_app.config['AVATARS_SAVE_PATH'], filename)
            if os.path.exists(path):  # not every filename map a unique file
                os.remove(path)


class Role(db.Model):
    __tablename__ = 'albumy_role'
    id = db.Column(db.Integer, primary_key=True)
    code = db.Column(db.String(21), unique=True)
    name = db.Column(db.String(21), unique=True)
    desc = db.Column(db.String(64), nullable=True)
    users = db.relationship('User', back_populates='role')
    permissions = db.relationship('Permission', secondary='albumy_roles_permissions', back_populates='roles')


class Permission(db.Model):
    __tablename__ = 'albumy_permission'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(21), unique=True)
    desc = db.Column(db.String(64), nullable=True)
    roles = db.relationship('Role', secondary='albumy_roles_permissions', back_populates='permissions')


roles_permissions = db.Table(
    'albumy_roles_permissions',
    db.Column('id', db.Integer, primary_key=True),
    db.Column('role_id', db.Integer, db.ForeignKey('albumy_role.id')),
    db.Column('permission_id', db.Integer, db.ForeignKey('albumy_permission.id'))
)


class Photo(db.Model):
    __tablename__ = 'albumy_photo'
    id = db.Column(db.Integer, primary_key=True)
    description = db.Column(db.String(500))
    filename = db.Column(db.String(64))
    filename_s = db.Column(db.String(64))
    filename_m = db.Column(db.String(64))
    flag = db.Column(db.Integer, default=0)  # 举报次数
    can_comment = db.Column(db.Boolean, default=True)
    timestamp = db.Column(db.DateTime, default=datetime.utcnow)
    user_id = db.Column(db.Integer, db.ForeignKey('albumy_user.id'))
    user = db.relationship(User, back_populates='photos')
    tags = db.relationship('Tag', back_populates='photos', secondary='albumy_photos_tags', cascade='all')

    collectors = db.relationship('Collect', back_populates='collected', cascade='all')  # 如:被收藏的数量
    comments = db.relationship('Comment', back_populates='photo', cascade='all')


# 为Photo创建一个数据库事件监听函数
@db.event.listens_for(Photo, 'after_delete', named=True)
def delete_photo_file(**kwargs):
    """删除photo对象时, 删除对应的文件"""
    """
        kwargs = 
            {'connection': <sqlalchemy.engine.base.Connection object at 0x0000025B138A7978>, 
             'mapper': <Mapper at 0x25b134fb2b0; Photo>, 
             'target': <Photo 8>
            }
        如果不加named=True, 需要传三个位置参数
    """
    target = kwargs['target']  # <Photo 8>
    for filename in [target.filename, target.filename_s, target.filename_m]:
        if filename is not None:
            path = os.path.join(current_app.config['ALBUMY_UPLOAD_PATH'], filename)
            if os.path.exists(path):
                os.remove(path)


class Tag(db.Model):
    __tablename__ = 'albumy_tag'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(12))
    desc = db.Column(db.String(64))
    photos = db.relationship(Photo, back_populates='tags', secondary='albumy_photos_tags')


photos_tags = db.Table(
    'albumy_photos_tags',
    db.Column('id', db.Integer, primary_key=True),
    db.Column('photo_id', db.Integer, db.ForeignKey('albumy_photo.id')),
    db.Column('tag_id', db.Integer, db.ForeignKey('albumy_tag.id')),
)


# 关联模型
class Collect(db.Model):
    __tablename__ = 'albumy_collect'
    # id = db.Column(db.Integer, primary_key=True)
    collector_id = db.Column(db.Integer, db.ForeignKey('albumy_user.id'), primary_key=True)  # 收藏者id
    collected_id = db.Column(db.Integer, db.ForeignKey('albumy_photo.id'), primary_key=True)  # 被收藏图片id
    timestamp = db.Column(db.DateTime, default=datetime.utcnow)
    collector = db.relationship('User', back_populates='collections', lazy='joined')
    collected = db.relationship('Photo', back_populates='collectors', lazy='joined')
    # __table_args__ = (
    #     db.UniqueConstraint('collector_id', 'collected_id', name='u_collector_id_collected_id'),
    #     # db.Index('ix_user_post_user_id_insert_time', 'user_id', 'insert_time'),
    # )


class Comment(db.Model):
    __tablename__ = 'albumy_comment'
    id = db.Column(db.Integer, primary_key=True)
    body = db.Column(db.Text)
    timestamp = db.Column(db.DateTime, default=datetime.utcnow, index=True)
    flag = db.Column(db.Integer, default=0)

    replied_id = db.Column(db.Integer, db.ForeignKey('albumy_comment.id'))
    user_id = db.Column(db.Integer, db.ForeignKey('albumy_user.id'))
    photo_id = db.Column(db.Integer, db.ForeignKey('albumy_photo.id'))

    photo = db.relationship('Photo', back_populates='comments')
    user = db.relationship('User', back_populates='comments')
    replies = db.relationship('Comment', back_populates='replied', cascade='all')  #
    replied = db.relationship('Comment', back_populates='replies', remote_side=[id])  #


class Notification(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    message = db.Column(db.Text)
    is_read = db.Column(db.Boolean, default=False)
    timestamp = db.Column(db.DateTime, default=datetime.utcnow, index=True)
    receiver_id = db.Column(db.Integer, db.ForeignKey('albumy_user.id'))
    receiver = db.relationship(User, back_populates='notifications')
完整models

 







 

转载于:https://www.cnblogs.com/yuqiangli0616/p/10266908.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值