前言
一个优秀的web框架一定是有一个较为成熟的orm机制的,比如python的django框架的djangoorm, java的Mybatis等都能实现较为全面的orm机制。flask虽然作为一个轻量级的web框架,当然也考虑到了这一点。但是flask并没有自己开发属于自己的orm生态,而是把sqlAlchemy 重新封装为了flask_sqlalchemy插件。
什么是ORM
ORM的全称为Object Relational Mapping,也简称O/RM,或O/R mapping是一种对象关系映射的计算机技术。是用于实现面向对象编程语言里不同类型系统的数据之间的转换 。简单的来说就是,数据库的记录就是一个对象,而数据库里的每一个字段对应的则是对象的属性。
flask 实现orm
安装准备
- flask
- mysql
- flask_sqlalchemy + pymysql
定义数据库模型
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Author(db.Model):
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), nullable=False,)
ages = Column(Integer)
address = Column(String(50))
如上所示,这里创建了一张Author表
连接数据服务器
映射到数据库有两步,第一步是绑定数据库路径,具体方法是在配置文件中配置,跟配置sqlalchemy的规则大体相同
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:root@localhost/flask_orm'
这里我选择的mysql数据库,当然也可以选择其他的数据库,具体可以参考文档flask-sqlchemy
创建数据库和表
方法一
import click
@app.cli.command()
def initdb():
db.create_all()
click.echo("数据表创建成功")
然后在命令行中输入 flask initdb即可,这种方法类似于脚步方法
方法二
在flask的核心对象初始化的时候,执行以下代码片段即可。
db.init_app(app)
with app.app_context():
db.create_all()
这样在flask定义的数据模型类就在数据库映射成对应的数据表
表的名字就是类名的小写,表的字段名则对应的是类的属性名,也可以进行修改。
class Author(db.Model):
__tablename__ = 'athors'
这样就是把命名对应的数据表为authors
简单的来说就是模型类(Author) --> 数据表(author)
模型类属性(name) --> 表字段(name)
表记录 -->类实例
crud操作
新增数据
author1 = Author(name="鲁迅", ages="55", address="北京")
author2 = Author(name="余华", ages="60", address="上海")
author3 = Author(name="菲茨杰拉德", ages="44", address="长岛")
db.session.add(author1)
db.session.add(author2)
db.session.add(author3)
db.session.commit()
这样数据就同步到了数据库中。
查询数据
这里我们在插入几条数据作为参考
author1 = Author(name="史铁生", ages="59", address="北京")
author2 = Author(name="老舍", ages="67", address="北京")
db.session.add_all([author1,author2])
db.session.commit()
这时候数据库一共有五条记录。
author = Author.query.all()
print(author)
[<Author 1>, <Author 2>, <Author 3>, <Author 4>, <Author 5>]
这种查询方式就是查询表Author中的所有记录
就相当于sql 语句 select * from author;
打印列表中每一个类实例就是一条数据库记录
为了方便观察,我把相关结果放到了http页面中,
authors = Author.query.all()
return render_template('read.html', authors=authors)
具体html代码如下
<table width="400" border="0" cellspacing="0" cellpadding="0">
<tr>
<td>姓名</td>
<td>年龄</td>
<td>地址</td>
</tr>
{% for author in authors %}
<tr>
<td>{{ author.name }}</td>
<td>{{ author.ages }}</td>
<td>{{ author.address }}</td>
</tr>
{% endfor %}
</table>
显示结果如下
当然我们也可以查询第一条数据
authors = Author.query.first()
print("姓名", authors.name)
print("年龄", authors.ages)
print("地址", authors.address)
当然我们也可以通过条件进行查找
authors = Author.query.filter_by(address="北京").all()
for author in authors:
print("姓名", author.name,end="")
print("年龄", author.ages,end="")
print("地址", author.address)
也可以多条件查询
authors = Author.query.filter_by(address="北京",ages=55).all()
这时候则只会有鲁迅一条记录了
当然还有更多复杂的查询功能,具体可以参考文档。
删除数据
author = Author.query.filter_by(address="北京",ages=55).first()
db.session.delete(author)
db.session.commit()
authors = Author.query.filter_by(address="北京").all()
for author in authors:
print("姓名", author.name,end="")
print("年龄", author.ages,end="")
print("地址", author.address)
这时候鲁迅这条记录就删除了
修改数据
author = Author.query.filter_by(address="北京",ages=67).first()
author.name = "舒庆春"
db.session.commit()
authors = Author.query.filter_by(address="北京").all()
for author in authors:
print("姓名", author.name,end="")
print("年龄", author.ages,end="")
print("地址", author.address)
数据表之间建立关系
一对多的关系
这种关系在软件设计中并不少见,建立关系的第一步是建立外键,外键(foreign key)用来在A表中存储B表的主键值以便和B表建立联系的关键字段,所以外键总是在多的这一侧来定义。
比如上面我们已经建立了"作者"表,每个作者都发表的多多少少有一些文章,这时候作者和文章就是一对多的关系。
class Book(db.Model):
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
category = Column(String(10))
author_id = Column(Integer,ForeignKey('author.id'))
author_id则为建立的关键字段
同时在Author表中需要建立关系属性字段,属性名一般和对应的“多”的那一侧的表名相似,当然也看自己的命名喜好。
class Author(db.Model):
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), nullable=False,)
ages = Column(Integer)
address = Column(String(50))
books = db.relationship('Book')
这个时候我们就可以在Book表新建一些数据
author = Author.query.filter_by(name="余华").first()
author_id = author.id
book1 = Book(title="活着", author_id=author_id)
book2 = Book(title="许三观卖血记", author_id=author_id)
book3 = Book(title="射雕英雄传", author_id=author_id)
db.session.add(book1)
db.session.add(book2)
db.session.add(book3)
db.session.commit()
for name in author.books:
print(name.title)
想要解除一对多的关系也较为简单
author = Author.query.filter_by(name="余华").first()
book = Book.query.filter_by(title="射雕英雄传").first()
author.books.remove(book)
db.session.commit()
for name in author.books:
print(name.title)
当然也可以建立双向关系
class Author(db.Model):
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), nullable=False,)
ages = Column(Integer)
address = Column(String(50))
books = db.relationship('Book',back_populates='author')
class Book(db.Model):
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
category = Column(String(10))
author_id = Column(Integer,ForeignKey('author.id'))
author = db.relationship('Author', back_populates='books')
author = Author.query.filter_by(name="余华").first()
for name in author.books:
print(name.title)
book = Book.query.filter_by(title="活着").first()
print("作者名称",book.author.name)
一对一关系
一对一关系其实是一对多关系的一些改动
class Author(db.Model):
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), nullable=False,)
ages = Column(Integer)
address = Column(String(50))
books = db.relationship('Book',back_populates='author')
wife = db.relationship('Wife',uselist=False)
class Wife(db.Model):
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), nullable=False, )
author_id = Column(Integer, ForeignKey('author.id'))
author = db.relationship('Author')
这里的wife表跟作者表为一对一的关系,这里面部考虑离婚再娶的关系。
author = Author.query.filter_by(name="余华").first()
wife = Wife(name="陈虹",author_id=author.id)
db.session.add(wife)
db.session.commit()
print("作者{}的妻子是{}".format(author.name,author.wife.name))
多对多的关系
多对多的关系其实是再新建一个关系表,来满足其的关系
这里暂且不提,再后面的数据表级联关系文章中再详细介绍
美中不足
任何一个框架都有他的优缺点,flask_sqlalchemy更是如此,一方面他表现了他的轻,包括sqlchemery本身带的线程池的优势。而另一方面他作为一个web的orm框架稍显不足,比如当
需要对数据表进行更新时是没有办法的,只能通过
db.drop_all()
db.create_all()
的方法其实就是把表删了,然后建立新的表。
这点就远不如django框架的灵活与强大的,包括各个关系的声明django框架也简单的多,轻就代表自由,代表多变,但是也代表需要我们做的事情更多,这也是python 的两代框架django和flask可以并驾齐驱的原因吧。