Flask-SQLAlchemy

参考学习了 阮一峰ORM 实例教程

面向对象编程把所有实体看成对象(object),关系型数据库则是采用实体之间的关系(relation)连接数据。ORM就是通过面向对象的思维去操作数据库,ORM 使用对象封装了数据库操作,因此可以不碰 SQL 语言。开发者只使用面向对象编程,与数据对象直接交互,不用关心底层数据库。

ORM 把数据库映射成对象:

  • 数据库的表(table) --> 类(class)
  • 记录(record,行数据)--> 对象(object)
  • 字段(field)--> 对象的属性(attribute)

许多语言都有自己的 ORM 库,最典型、最规范的实现公认是 Ruby 语言的 Active Record。Active Record 对于对象和数据库表的映射,有一些命名限制。

(1)一个类对应一张表。类名是单数,且首字母大写;表名是复数,且全部是小写。比如,表books对应类Book

(2)如果名字是不规则复数,则类名依照英语习惯命名,比如,表mice对应类Mouse,表people对应类Person

(3)如果名字包含多个单词,那么类名使用首字母全部大写的骆驼拼写法,而表名使用下划线分隔的小写单词。比如,表book_clubs对应类BookClub,表line_items对应类LineItem

(4)每个表都必须有一个主键字段,通常是叫做id的整数字段。外键字段名约定为单数的表名 + 下划线 + id,比如item_id表示该字段对应items表的id字段。

SQLAlchemy是一个强大的关系型数据库框架,提供了高层ORM,也提供了使用数据库原生SQL的功能,Flask-SQLAlchemy是一个Flask扩展,简化了SQLAlchemy的操作。

SQLAlchemy创建的数据库实例为模型提供了一个基类以及一系列的辅助类、辅助函数,可用于定义模型的数据结构。

一对多:

 1、定义模型

  比如:一个Role对应对应多个User, 一个User对应多个Post

#定义模型
class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.INTEGER, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    
    def __repr__(self):
        return self.name

class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.INTEGER, primary_key=True)
    username = db.Column(db.String(64), unique=True, index=True)
   
    def __repr__(self):
        return self.username

 

2、建立关系

class Role(db.Model):
    #......
    # relationship给Role类增加了一个users属性,内容是以role.id为关联的一组User,
    # backref='role'给User类增加了一个role属性,内容是以role.id为关联的Role,可以代替role_id访问Role模型(通过u.role),否则只能用u.role_id
   # 第一个参数是建立关系的表名称 users = db.relationship('User', backref='role') class User(db.Model): #...... role_id = db.Column(db.INTEGER, db.ForeignKey('roles.id')) #定义关系,在多的一侧加入一个外键关联,指向“一”这一侧的记录,对应到Role中的id,建立“一对多”的关系,

 

3、准备数据

def insert_data():
    admin_role = Role(name='Admin')
    mod_role = Role(name='Mod')
    user_role = Role(name='User')
  #如果没有backref='role',则只能通过role_id=1来插入数据
    user_join = User(username='join', role=admin_role)
    user_susan = User(username='susan', role_id=3)
    user_david = User(username='david', role=user_role)
    db.session.add_all([admin_role, mod_role, user_role, user_join, user_susan, user_david])

if __name__ == '__main__':
    insert_data()
    db.session.commit()

 

4、查询

for r in Role.query.all():
    print('Role自身数据:{},{}'.format(r.id, r.name))
    print('r.user:{}'.format(r.users))# relationship给Role类增加了一个users属性,内容是以role.id为关联的一组User

for u in User.query.all():
    print('User自身数据:{},{}'.format(u.id, u.username))
    print('正向,外键foreignkey作用:role_id={},role={}'.format(u.role_id, u.role))
    # backref='role'给User类增加了一个role属性,内容是以role.id为关联的Role。
    print('反向,backfer字段:b.role.name={}'.format(u.role.name))

 

5、可以在视图函数中直接操作数据库

 

多对多:

多对多关系可以分解成原表和关联表之间的两个多对一的关系,通过一个关联表将两个表关联起来。

1、建立模型

# 关联表,第一个参数为中间表的表名,然后在表中添加两个字段,这两个字段分别为关联的两个表的一对多的外键,也同为主键
# 关联表就是一个简单的表,不是模型,SQLAlchemy会自动接收这个表
registrations =db.Table('registrations', db.Column('student_id', db.INTEGER, db.ForeignKey('students.id'), primary_key=True), db.Column('class_id', db.INTEGER, db.ForeignKey('classes.id'), primary_key=True)) class Student(db.Model): __tablename__ = 'students' id = db.Column(db.INTEGER, primary_key=True) name = db.Column(db.String(64)) # 多对多可以在任意一方中添加一个relationship字段,第一个参数为关联的另外一个表, # 第二个参数为中间表的表名,第三个参数backref是反向查询时的关键字(这里给Student增加了一个classes属性,给Class增加了一个students属性) classes = db.relationship('Class', secondary=registrations, backref='students', lazy='dynamic') def __repr__(self): return self.name class Class(db.Model): __tablename__ = 'classes' id = db.Column(db.INTEGER, primary_key=True) name = db.Column(db.String(64)) def __repr__(self): return self.name

 

 2、准备数据

def insert_data2():
    stu1 = Student(name='cc')
    stu2 = Student(name='dd')
    cur1 = Class(name="python")
    cur2 = Class(name="c")
    cur3 = Class(name="java")
    # 关联数据,给student添加classes属性
    stu1.classes.append(cur1)
    stu1.classes.append(cur2)
    stu2.classes.append(cur2)
    stu2.classes.append(cur3)
    # 添加到数据库
    db.session.add_all([stu1, stu2, cur1, cur2, cur3])
    db.session.commit()

 

3、查询

# 通过student查询classes
courses = Student.query.filter_by(name='dd').first().classes.all()#这里最后的all()是因为前面的lazy='dynamic'
print('dd的courses:', courses)

# 通过classes查询students
stus = Class.query.filter_by(name='c').first().students
print('c课程stuents:', stus)

 

自引用关系:

  学生和课程的多对多关系中,是两个实例之间的多对多关系。

  但是关注用户存在的多对多关系,只有用户一个实例。如果关系中的两侧都在同一个表中,这种关系成为自引用关系。

1、关联表的模型 

# 多对多关系,关联表完全是由SQLALchemy掌控的内部表,为了能在关系中处理自定义的数据,提升关联表的地位,使用程序可访问的模型
# 要把这个多对多关系的左右两侧拆分成两个基本的一对多关系
class Follow(db.Model):
    __tablename__ = 'follows'
    # follower_id、followed_id外键关联的都是users.id,因为是只有一个实例的自引用关系
    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)

 

2、在User模型中增加外键关联,实现两个一对多的关系

# 用户模型
class User(UserMixin, db.Model):
    #...
    # 把这个多对多关系的左右两侧拆分成两个基本的一对多关系
    # 建立一对多关系,给User增加一个followed属性(被关注的),给Follow增加一个follower属性(关注者)
    followed = db.relationship('Follow',
                               foreign_keys=[Follow.follower_id],#为了消除外键之间的歧义,建立关系是使用foreign_keys指定的外键
                               backref=db.backref('follower', lazy='joined'),#回引Follow模型,joined实现立即从联结查询中加载相关对象
                               lazy='dynamic',#不会直接返回记录,而是返回查询对象,可在其上添加过滤器
                               cascade='all, delete-orphan')#配置在父对象上执行的操作对相关对象的影响
    # 建立一对多关系,给User增加一个followers属性,给Follow增加一个followed属性
    followers = db.relationship('Follow',
                               foreign_keys=[Follow.followed_id],#为了消除外键之间的歧义,建立关系是使用foreign_keys指定的外键
                               backref=db.backref('followed', lazy='joined'),#回引Follow模型,joined实现立即从联结查询中加载相关对象
                               lazy='dynamic',
                               cascade='all, delete-orphan')

 

 3、增加数据

david = User.query.filter_by(username='david').first() #id=1
susan = User.query.filter_by(username='susan').first() #id=2
john = User.query.filter_by(username='john').first()   #id=3
john.follow(david)
susan.follow(john)
susan.follow(david)

 

follows表如下:

follower_id followed_id timestamp
1 3 2020-04-19 13:23:36
2 1 2020-04-19 13:23:39
2 3 2020-04-19 13:23:45

 

 

 

 

 

4、查询

 

john.is_following(david)    #True
david.is_followed_by(john)  #True
david.is_followed_by(susan) #True
john.is_following(susan)    #False
john.followed.count()#1
susan.followed.count()  #2  被susan关注的人
susan.followers.count()  #0 关注susan的人

j = john.followed.first() #得到的是一个Follow模型
j.followed_id  #3--------->followed:david
j.follower_id   #1--------->follower:john

for i in susan.followed.all():
    print(i.followed_id)  #1 3  被susan关注的人
for j in david.followers.all():
    print(j.follower_id)  #1 2  关注daivd的人

 

posted @ 2020-03-29 09:25  cheng4632  阅读(265)  评论(0编辑  收藏  举报