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
flask中models设计的更多相关文章
- Flask 中的MTV架构之Models
Flask 中的MTV架构之Models 1.Models(数据模型) 1.1 flask-sqlalchemy(数据库) 说明:提供了大多数关系型数据库的支持,而且提供了ORM # 安装: pi ...
- flask框架----整合Flask中的目录结构
一.SQLAlchemy-Utils 由于sqlalchemy中没有提供choice方法,所以借助SQLAlchemy-Utils组件提供的choice方法 import datetime from ...
- 整合Flask中的目录结构
一.SQLAlchemy-Utils 由于sqlalchemy中没有提供choice方法,所以借助SQLAlchemy-Utils组件提供的choice方法 import datetime from ...
- Flask学习【第11篇】:整合Flask中的一些知识点
SQLAlchemy-Utils 由于sqlalchemy中没有提供choice方法,所以借助SQLAlchemy-Utils组件提供的choice方法 import datetime from sq ...
- Flask系列(十一)整合Flask中的目录结构(sqlalchemy-utils)
一.SQLAlchemy-Utils 由于sqlalchemy中没有提供choice方法,所以借助SQLAlchemy-Utils组件提供的choice方法 import datetime from ...
- Flask【第11篇】:整合Flask中的目录结构
整合Flask中的目录结构 一.SQLAlchemy-Utils 由于sqlalchemy中没有提供choice方法,所以借助SQLAlchemy-Utils组件提供的choice方法 import ...
- Flask 中的 SQLAlchemy 使用教程
Flask 是一个 python web micro framework.所谓微框架,主要是 flask 简洁与轻巧,自定义程度高.相比 django 更加轻量级. 之前一直折腾 django,得益于 ...
- flask中的蓝图与红图
内容: 1.flask中的蓝图 2.flask子域名实现 3.flask中的红图 1.flask中的蓝图 一个大型项目中视图比较多,如果仅仅是写在app.py中不方便管理,蓝图就可以做到分功能分目录结 ...
- Flask中的ORM使用
前言 ORM拓展 安装 数据库设置 使用 关系 单表操作 建表 应用表结构 CRUD 添加查找操作 更新操作 删除操作 一对多 多对多 总结 前言 最近几天接触了一下Flask,在惊叹于其简洁性的同时 ...
随机推荐
- 【原创】大数据基础之ETL vs ELT or DataWarehouse vs DataLake
ETL ETL is an abbreviation of Extract, Transform and Load. In this process, an ETL tool extracts the ...
- 使用原生node.js搭建HTTP服务器,支持MP4视频、图片传输,支持下载rar文件
前言 如何安装node.js,如何搭建一个简易的http服务器我这里就不再赘述了,不懂的同学可以先去学习一下.当然了,我写的也就属于简易版的增强版,大家有什么高见的欢迎提出,然后进入正题. 目录结构 ...
- 什么是NoSQL,为什么要使用NoSQL?
详见: https://blog.csdn.net/a909301740/article/details/80149552 https://baike.so.com/doc/5569749-57849 ...
- redis-cluster集群总结
Redis集群搭建 要想搭建一个最简单的Redis集群,那么至少需要6个节点:3个Master和3个Slave.为什么需要3个Master呢?如果你了解过Hadoop/Storm/Zookeeper这 ...
- Asp.Net Zero通用打印实现
Asp.Net Zero是一款非常优秀的web框架,可以用来快速构建业务系统.框架满足了业务系统所需的大部分通用功能,但是系统必须的打印报表功能一直没有实现.下面给大家介绍如何在zero中集成打印功能 ...
- Nginx中虚拟主机配置
一.Nginx中虚拟主机配置 1.基于域名的虚拟主机配置 1.修改宿主机的hosts文件(系统盘/windows/system32/driver/etc/HOSTS) linux : vim /etc ...
- 关于layui表格渲染templet解析单元格的问题
原文链接:https://blog.csdn.net/wyp_comeon/article/details/81735951关于表格解析自定义单元格的解析参数请先详细查看官方文档:http://www ...
- zeus部署
1.下载zeus 阿里在github上已经不维护zeus了,在网上找到一个别人贡献的 https://github.com/michael8335/zeus2 下载下来 通过shell rz命令上传到 ...
- Ruby2.0后版本的debug工具: byebug
https://github.com/deivid-rodriguez/byebug/blob/master/GUIDE.md 安装: gem install byebug 使用: Rails: 直接 ...
- Echarts 饼状图 字体重叠问题
原理:设置最小扇形的大小,把他撑起来 在 series 里 使用 minAngle: 38, //最小的扇区角度(0 ~ 360),用于防止某个值过小导致扇区太小影响交互 角度自己调好就可以了 个人笔 ...