flask数据库
# ORM
Web 应用 中使用 原生 sql
的问题:
- 手动编写
SQL
语句低效,而且视图函数中加入太多SQL
语句会降低代码的易读性。另外还会容易出现安全问题,比如SQL注入。 - 常见的开发模式是在开发时使用简单的
SQLite
,而在部署时切换到MySQL
等更健壮的DBMS。但是对于不同的DBMS,我们需要使用不同的Python接口库,这让DBMS的切换变得不太容易。 - 注意 尽管使用
ORM
可以避免SQL
注入问题,但你仍然需要对传入的查询参数进行验证。另外,在执行原生SQL语句时也要注意避免使用字符串拼接或字符串格式化的方式传入参数。
ORM
可以避免大部分问题。
ORM
把底层的 SQL
数据实体转化成高层的Python对象。
ORM
主要实现了三层映射关系:
- 表→Python类。
- 字段(列)→类属性。
- 记录(行)→类实例。
ORM
优点:
- 便于使用
- 灵活性好
- 提升开发效率, 原生
SQL
的性能效率是比ORM
高,但是损失一点性能效率来提升开发效率 - 可移植性高,
ORM
支持多种DBMS
, 包括MySQL
,Oracle
等
# 使用 ORM
# sql
# CREATE TABLE contacts(
# name varchar(100) NOT NULL,
# phone_number varchar(32),
# );
# ORM
from foo_orm import Model, Column, String
class Contact(Model):
__tablename__ = 'contacts'
name = Column(String(100), nullable=False)
phone_number = Column(String(32))
# 插入 contacts
# INSERT INTO contacts(name, phone_number)
# VALUES('Grey Li', '12345678');
# 等价于
# ORM
contact = Contact(name='Grey Li', phone_number='12345678')
# 连接数据库服务器
# URI
sqlite:////tmp/test.db
, sqlite
mysql://username:password@server/db
, mysql
# 字段类型
字段 | 说明 |
---|---|
Integer | 整数 |
String(n) | 字符串, n 设置最大长度 |
Text | Unicode文本 |
Date | 日期, Python datetime.date |
Time | 时间, Python datetime.time |
DateTime | 时间和日期, Python datetime.timedelta |
Float | 浮点数 |
Boolean | 布尔数 |
PickleType | Pickle 列化的 Python 对象 |
LargeBinary | 任意二进制数据 |
# 字段参数
字段 | 说明 |
---|---|
primary_key | 如果设为 True, 该字段为主键 |
unique | 如果设为 True, 该字段不允许出现重复值 |
index | 如果设为 True, 为该字段创建索引,提高查询效率 |
nullable | 确定字段值可否为空,值为 True 或 False, 默认值是 True |
default | 为字段设置默认值 |
# 表名
默认是 模型类的名称, 如果想指定表名称, 通过 __tablename__
属性指定
# 数据库操作
# create, 增
>>> from app import db, Note
>>> note1 = Note(body='remember Sammy Jankis')
>>> note2 = Note(body='SHAVE')
>>> note3 = Note(body='DON\'T BELIEVE HIS LIES, HE IS THE ONE, KILL HIM')
>>> db.session.add(note1)
>>> db.session.add(note2)
>>> db.session.add(note3)
>>> db.session.commit()
db.session.add()
, 添加一条记录db.session.add_all()
, 一次性添加多条记录db.session.commit()
, 提交会话
# Read, 查
查询语法,<模型类>.query.<过滤方法>.<查询方法>
。
常用的SQLAlchemy查询方法
常用查询方法 | 说明 |
---|---|
all() | 返回包含所有查询记录的列表 |
first() | 返回查询的第一条记录,如果未找到,则返回 None |
one() | 返回第一条记录,且仅运行只有一条。如果查询结果数量大于小于1,则抛出错误 |
get(id) | 传入主键值作为参数,返回指定主键值的记录,如果未找到,则返回 None |
count() | 返回查询结果的数量 |
with_parent(instance) | 出入模型类实例作为参数,返回和这个实例相关联的对象 |
paginate() | 返回一个 Pagination 对象, 可以对记录进行分页处理 |
常用过滤方法 | 说明 |
---|---|
filter() | 使用指定的规则过滤记录 |
filter_by() | 返回指定规则过滤记录(关键字) |
order_by() | 根据指定条件对记录进行排序 |
limit(limit) | 使用指定的值限制原查询返回的记录数量 |
group_by() | 根据指定条件对记录进行分组 |
offset(offset) | 使用指定的值偏移原查询的结果 |
# update, 改
>>> note = Note.query.get(2)
>>> note.body
u'SHAVE'
>>> note.body = 'SHAVE LEFT THIGH'
>>> db.session.commit()
# delete, 删除
>>> note = Note.query.get(2)
>>> db.session.delete(note)
>>> db.session.commit()
# 定义关系
# 一对多
class Author(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), unique=True)
phone = db.Column(db.String(20))
# 关系
articles = db.relationship('Article') # collection
def __repr__(self):
return '<Author %r>' % self.name
class Article(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(50), index=True)
body = db.Column(db.Text)
# 外键
author_id = db.Column(db.Integer, db.ForeignKey('author.id'))
articles = db.relationship('Article')
,这个属性并没有使用Column类声明为列,而是使用了db.relationship()
关系函数定义为关系属性,因为这个关系属性返回多个记录,我们称之为集合关系属性。
relationship()
函数的第一个参数为关系另一侧的模型名称,它会告诉 SQLAlchemy
将 Author
类与 Article
类建立关系。
当这个关系属性被调用时,SQLAlchemy
会找到关系另一侧(即article
表)的外键字段(即author_id
),
然后反向查询article
表中所有author_id
值为当前表主键值(即author.id
)的记录,返回包含这些记录的列表,
也就是返回某个作者对应的多篇文章记录。
author_id = db.Column(db.Integer, db.ForeignKey('author.id'))
, 外键定义
# 多对多
# 交给 orm 管理
association_table = db.Table('association',db.Column('student_id', db.Integer, db.ForeignKey('student.id')),db.Column('teacher_id', db.Integer, db.ForeignKey('teacher.id')))
class Student(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(70), unique=True)
grade = db.Column(db.String(20))
teachers = db.relationship('Teacher',
secondary=association_table,
back_populates='students')
class Teacher(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(70), unique=True)
office = db.Column(db.String(20))
关联表使用db.Table
类定义,传入的第一个参数是关联表的名称。
我们在关联表中定义了两个外键字段:teacher_id
字段存储Teacher
类的主键,student_id
存储Student
类的主键。