ORM 全拼 Object-Relation Mapping. 中文意为 对象-关系映射. 主要实现模型对象到关系数据库数据的映射. 和Java中的JDBC 有异曲同工之处
优点
缺点
关系的分类
如图, 一个角色可以有多个用户. 如果用户是管理员角色, 就不能是普通用户; 如果用户是VIP会员, 就不能是普通用户.
db_model.py
from datetime import datetime
from application import db
# 设置基类方便管理公共字段
class BaseModel(object):
id = db.Column(db.Integer, primary_key=True) # 主键
is_del = db.Column(db.Boolean, default=False) # 默认为False,不删除/显示;当为True时,删除/不显示
create_time = db.Column(db.DateTime, default=datetime.now) # 记录的创建时间
update_time = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now) # 记录的更新时间
# 多继承,通过列表或者元组继承,坏处:父类属性不提示
base_db_model = (BaseModel, db.Model)
# 角色类
class Role(db.Model):
"""角色类"""
__tablename__ = "role"
id = db.Column(db.Integer, primary_key=True) # 角色id
name_role = db.Column(db.String(32)) # 角色名称
# 关联引用 Role关联User
# db.relationship("要关联的数据库模型类", lazy='dynamic')
list_user = db.relationship("User", lazy='dynamic')
# 用户表, 通过拆包base_db_model 多继承
class User(*base_db_model):
"""用户表"""
__tablename__ = "user"
name_nick = db.Column(db.String(32), nullable=False) # 昵称
password_hash = db.Column(db.String(128), nullable=False) # 密码
mobile = db.Column(db.String(128), unique=True, nullable=False) # 手机号码
id_role = db.Column(db.Integer, db.ForeignKey("role.id"), default=2) # 角色id
在role 指定了一个单向的关联关系, role 关联user, 那么怎么让user 关联role?如下
class User(*base_db_model):
"""用户表"""
__tablename__ = "user"
name_nick = db.Column(db.String(32), nullable=False) # 昵称
password_hash = db.Column(db.String(128), nullable=False) # 密码
mobile = db.Column(db.String(128), unique=True, nullable=False) # 手机号码
id_role = db.Column(db.Integer, db.ForeignKey("role.id"), default=2) # 角色id
# 添加关联关系 user关联role
role = db.relationship("Role", lazy='dynamic')
合并,将两个单向的关联关系合并成一个双向的关系
# 角色类
class Role(db.Model):
"""角色类"""
__tablename__ = "role"
id = db.Column(db.Integer, primary_key=True) # 角色id
name_role = db.Column(db.String(32)) # 角色名称
# 通过relationship 指定正向引用, backref 指定反向引用 ,构成一个双向的引用关系
list_user = db.relationship("User", backref="role" , lazy='dynamic')
# 用户表, 通过拆包base_db_model 多继承
class User(*base_db_model):
"""用户表"""
__tablename__ = "user"
name_nick = db.Column(db.String(32), nullable=False) # 昵称
password_hash = db.Column(db.String(128), nullable=False) # 密码
mobile = db.Column(db.String(128), unique=True, nullable=False) # 手机号码
id_role = db.Column(db.Integer, db.ForeignKey("role.id"), default=2) # 角色id
运行结果
如图, 因为用户有角色, 当用户(角色) 发布新闻, 经过编辑(角色) 审核. 通过审核之后, 如果出现什么问题, 责任人是编辑, 而不是用户. 如果有以上的需求就出现了news 表的id_author 和id_charge_editor 字段的外键都是user 表的主键. 数据库是允许这样设计的, 那么SQLAlchemy 中怎样表示这样的关系?
# 一个用户可以发布多篇新闻
list_news = db.relationship("News", foreign_keys=[News.id_author], backref="author", lazy="dynamic")
# 一个编辑可以编辑多篇新闻
list_edit_news = db.relationship("News", foreign_keys=[News.id_charge_editor], backref="editor", lazy="dynamic")
和一个表中只有一个外键是另一张表中的主键类似, relationship 指定关系引用, backref 指定反向的关系引用. 区别在于foreign_keys, foreign_keys 指定关系引用作用的于哪个外键, 或者说关系引用作用于主键与哪个外键之间的关联关系.
如图, 用户和评论之间是一个多对多的关系, 一个用户可以给多条评论点赞, 一条评论可以被多个用户点赞. 从图上可以很容易的看出, 多对多是由两个一对多构成的, 并且有一张表存储着是多对多的关系.
数据库模型
# 评论点赞
class CommentPraise(*base_db_model):
"""评论点赞"""
__tablename__ = "comment_praise"
id_comment = db.Column(db.Integer, db.ForeignKey("comment.id")) # 评论id
id_user = db.Column(db.Integer, db.ForeignKey("user.id")) # 用户id
# 评论表
class Comment(*base_db_model):
"""评论表"""
__tablename__ = "comment"
id_user = db.Column(db.Integer, db.ForeignKey("user.id")) # 用户id
id_news = db.Column(db.Integer, db.ForeignKey("news.id")) # 新闻id
id_parent = db.Column(db.Integer, db.ForeignKey("comment.id")) # 父评论id
content = db.Column(db.Text, nullable=False) # 评论内容
praise_num = db.Column(db.Integer, default=0) # 点赞数(喜欢数)
# 用户表
class User(*base_db_model):
"""用户表"""
__tablename__ = "user"
name_nick = db.Column(db.String(32), nullable=False) # 昵称
password_hash = db.Column(db.String(128), nullable=False) # 密码
mobile = db.Column(db.String(128), unique=True, nullable=False) # 手机号码
id_role = db.Column(db.Integer, db.ForeignKey("role.id"), default=2) # 角色id
last_login = db.Column(db.String(32), default=datetime.now) # 最后登录时间
is_login = db.Column(db.Boolean, default=False) # 是否登录
gender = db.Column(db.Enum("man", "woman"), default="man")
last_login_ip = db.Column(db.String(64)) # 最后登录ip
avatar_url = db.Column(db.String(256)) # 头像
signature = db.Column(db.String(512)) # 个性签名
# 当前用户发布的所有评论
list_user_comment = db.relationship("Comment", backref="user", lazy="dynamic")
# 当前用户点赞的所有评论
list_user_praise_comment = db.relationship("Comment", secondary="comment_praise",
# 当前评论点赞的所有用户
backref=db.backref('list_praise_comment_user', lazy='dynamic'),
lazy="dynamic")
如上数据库模型, 在user 类中建立与Comment 的一对多关系引用.
对于多对多的关系
另一种写法
# 通过Table 类得到Table 类的对象association_table
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id'))
)
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child",
# secondary = association_table Table类的对象
secondary=association_table,
backref="parents")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
secondary 常见方式是使用Table 类的对象作为值, 也可以使用表名字符串作为值.
暂时未找到通过Table 作为数据库模型怎样继承基类的方式, 所以暂时算作是一种缺陷. 使用继承db.Model 的方式可以方便继承.
如图,新闻类型和新闻分类是一个多对多关系, 一个类型可以有多个分类的新闻, 一个分类可以有多个类型的新闻. 刚刚建立这个关系的时候一脸蒙圈, 关系存放在哪里? 单独摘出这一部分瞬间明白了, news 本身就是一个表, 当然可以作为type 与category 的关联关系表了, 不需要额外创建第四张表.
# 新闻类型
class NewsType(db.Model):
"""新闻类型"""
__tablename__ = "news_type"
id = db.Column(db.Integer, primary_key=True) # 新闻类型id
name_type = db.Column(db.String(32)) # 新闻类型
list_news = db.relationship("News", backref="news_type", lazy='dynamic')
# 当前新闻类型所属于的所有分类
list_news_category = db.relationship('NewsCategory', secondary="news",
backref=db.backref('list_news_type', lazy='dynamic'),
lazy='dynamic')
# 新闻分类
class NewsCategory(db.Model):
"""新闻分类"""
__tablename__ = "news_category"
id = db.Column(db.Integer, primary_key=True) # 新闻类型id
name_type = db.Column(db.String(32)) # 新闻类型
list_news = db.relationship("News", backref="news_category", lazy='dynamic')
按照多对多的规则, 在NewsType 或者 NewsCategory 定义一个双向的关系引用. 这里在NewsType 定义了关系引用, 关联关系存放在News 表中.
一个父评论可以有多个子评论, 评论表的一对多自关联
# 评论表
class Comment(*base_db_model):
"""评论表"""
__tablename__ = "comment"
id_user = db.Column(db.Integer, db.ForeignKey("user.id")) # 用户id
id_news = db.Column(db.Integer, db.ForeignKey("news.id")) # 新闻id
id_parent = db.Column(db.Integer, db.ForeignKey("comment.id")) # 父评论id
content = db.Column(db.Text, nullable=False) # 评论内容
praise_num = db.Column(db.Integer, default=0) # 点赞数(喜欢数)
parent = db.relationship("Comment", remote_side="comment.c.id",
backref=db.backref('childs', lazy='dynamic'))
自关联一对多, 其本质也是一对多, 只不过两端都是一张表, 所以和一对多基本类似.
如图, 一个用户可以有多个粉丝, 一个用户可以被多个人关注, 用户表的多对多自关联
# 用户粉丝表
class FollowsUser(*base_db_model):
"""用户粉丝表"""
__tablename__ = "user_follows"
id_user = db.Column(db.Integer, db.ForeignKey("user.id")) # 用户id
id_follower = db.Column(db.Integer, db.ForeignKey("user.id")) # 新闻id
# 用户表
class User(*base_db_model):
"""用户表"""
__tablename__ = "user"
name_nick = db.Column(db.String(32), nullable=False) # 昵称
password_hash = db.Column(db.String(128), nullable=False) # 密码
mobile = db.Column(db.String(128), unique=True, nullable=False) # 手机号码
id_role = db.Column(db.Integer, db.ForeignKey("role.id"), default=2) # 角色id
last_login = db.Column(db.String(32), default=datetime.now) # 最后登录时间
is_login = db.Column(db.Boolean, default=False) # 是否登录
gender = db.Column(db.Enum("man", "woman"), default="man")
last_login_ip = db.Column(db.String(64)) # 最后登录ip
avatar_url = db.Column(db.String(256)) # 头像
signature = db.Column(db.String(512)) # 个性签名
followers = db.relationship('User',
secondary="user_follows",
primaryjoin="user.c.id == user_follows.c.id_user",
secondaryjoin="user.c.id == user_follows.c.id_follower",
backref=db.backref('user', lazy='dynamic'),
lazy='dynamic')
自关联多对多, 其本质也是多对多, 只不过两端都是一张表, 所以和多对多基本类似, 同时多对多是两个一对多组成的, 并且是自关联, 所以类似于两个自关联一对多.
class Role(db.Model):
"""角色表"""
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
users = db.relationship('User', backref='role', lazy='dynamic')
class User(db.Model):
"""用户表"""
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
通过Table 类
tb_student_course = db.Table('tb_student_course',
db.Column('student_id', db.Integer, db.ForeignKey('students.id')),
db.Column('course_id', db.Integer, db.ForeignKey('courses.id'))
)
class Student(db.Model):
__tablename__ = "students"
id = db.Column(db.Integer, primary_key=True)
courses = db.relationship('Course', secondary=tb_student_course,
backref=db.backref('students', lazy='dynamic'),
lazy='dynamic')
class Course(db.Model):
__tablename__ = "courses"
id = db.Column(db.Integer, primary_key=True)
通过Model
# 评论点赞 db SQLAlchemy的实例对象
class CommentPraise(db.Model):
"""评论点赞"""
__tablename__ = "comment_praise"
id_comment = db.Column(db.Integer, db.ForeignKey("comment.id")) # 评论id
id_user = db.Column(db.Integer, db.ForeignKey("user.id")) # 用户id
# 评论表
class Comment(db.Model):
"""评论表"""
__tablename__ = "comment"
id = db.Column(db.Integer, primary_key=True)
# 用户表
class User(db.Model):
"""用户表"""
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True)
# 当前用户点赞的所有评论
list_user_praise_comment = db.relationship("Comment", secondary="comment_praise",
# 当前评论点赞的所有用户
backref=db.backref('list_praise_comment_user', lazy='dynamic'),
lazy="dynamic")
class Comment(db.Model):
"""评论"""
__tablename__ = "comments"
id = db.Column(db.Integer, primary_key=True)
parent_id = db.Column(db.Integer, db.ForeignKey("comments.id"))
parent = db.relationship("Comment", remote_side=[id],
backref=db.backref('childs', lazy='dynamic'))
通过Table
tb_user_follows = db.Table(
"tb_user_follows",
db.Column('follower_id', db.Integer, db.ForeignKey('info_user.id'), primary_key=True), # 粉丝id
db.Column('followed_id', db.Integer, db.ForeignKey('info_user.id'), primary_key=True) # 被关注人的id
)
class User(db.Model):
"""用户表"""
__tablename__ = "info_user"
id = db.Column(db.Integer, primary_key=True)
# 用户所有的粉丝,添加了反向引用followed,代表用户都关注了哪些人
followers = db.relationship('User',
secondary=tb_user_follows,
primaryjoin=id == tb_user_follows.c.followed_id,
secondaryjoin=id == tb_user_follows.c.follower_id,
backref=db.backref('followed', lazy='dynamic'),
lazy='dynamic')
通过Model
# 用户粉丝表
class FollowsUser(db.Model):
"""用户粉丝表"""
__tablename__ = "user_follows"
id_user = db.Column(db.Integer, db.ForeignKey("user.id")) # 用户id
id_follower = db.Column(db.Integer, db.ForeignKey("user.id")) # 新闻id
# 用户表
class User(db.Model):
"""用户表"""
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True)
followers = db.relationship('User', secondary="user_follows",
primaryjoin="user.c.id == user_follows.c.id_user",
secondaryjoin="user.c.id == user_follows.c.id_follower",
backref=db.backref('user', lazy='dynamic'),
lazy='dynamic')
到此结 DragonFangQy 2018.7.12