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,在惊叹于其简洁性的同时 ...
随机推荐
- JS ES5
常用 严格模式 use strict 必须使用var声明变量 禁止自定义函数this指向window 'use strict' funcion Person(name){ this.name = na ...
- js之数据类型(对象类型——构造器对象——函数1)
函数它只定义一次,但可能被多次的执行和调用.JavaScript函数是参数化的,函数的定义会包括形参和实参.形参相当于函数中定义的变量,实参是在运行函数调用时传入的参数. 一.函数定义 函数使用fun ...
- C# 知识点笔记:IEnumerable<>的使用,利用反射动态调用方法
IEnumerable<T>的使用 创建一个IEnumerable对象 List<string> fruits = new List<string> { " ...
- kbmMemTable中怎么根据UniqueRecID定位到对应的记录
function TForm5.LocateUniqueRecID(aDataSet: TkbmMWCustomClientQuery; AID: TkbmNativeInt): Boolean; v ...
- MySQL增删改查语句
创建数据库:CREATE DATABASE 数据库名; 创建数据表:CREATE TABLE table_name (column_name column_type); 插入数据:INSERT INT ...
- Win7系统开机速度慢怎么解决?
Win7系统使用时间长了,我们就会发现电脑的开机启动速度变慢了,其实除了关闭相应的启动项之外,我们还可以对电脑进行相关的系统配置,来使电脑能够更加快速的启动.下面好系统重装助手就来告诉你怎么解决Win ...
- 《python解释器源码剖析》第9章--python虚拟机框架
9.0 序 下面我们就来剖析python运行字节码的原理,我们知道python虚拟机是python的核心,在源代码被编译成字节码序列之后,就将有python的虚拟机接手整个工作.python虚拟机会从 ...
- sprintf的使用
头文件:stdio.h 函数原型:int sprintf(char *buffer, const char *format, [argument]…) 参数: (1)buffer:是char类型的指针 ...
- STM32WB 信息块之OTP
1.OTP Area范围:0x1FFF 7000 - 0x1FFF 73FF 大小1 K 2.OTP描述 1 KB (128 double words) OTP (one-time programma ...
- mongodb单机搭建
参考网站:http://www.runoob.com/mongodb/mongodb-linux-install.html 1.下载 https://www.mongodb.com/download- ...