目录
一、Python 操作 MySQL常见方式
二、Flask-SQLAlchemy 扩展
1、一个最小应用
2、一对多(one-to-many)关系:ForeignKey
用 Python 操作 MySQL很多种方式,比如:mysql-connecto、MySQLdb、mysqlclient、PyMySQL、peewee 和 SQLAIchemy 等,其中 mysql-connector是 MySQL 官方提供的驱动器,用来给后端语言,比如 Python 提供连接,这种基本就是写SQL语句的方式 ,简单的场景可以满足。
SQLAIchemy是使用ORM的方式,ORM 的英文是 Object Relational Mapping,也就是采用对象关系映射的模式,使用这种模式可以将数据库中各种数据表之间的关系映射到程序中的对象。这种模式可以屏蔽底层的数据库的细节,不需要我们与复杂的 SQL 语句打交道,直接采用操作对象的形式操作就可以。
不过如果应用数据实体少,其实没有必要使用 ORM 框架,针对少量对象的管理,自己实现起来也很简单,比如本篇文章中我讲到的采用官方提供的 mysql-connector 驱动的方式来实现 CRUD。引入一个框架的学习成本很高,代码膨胀也很厉害,所以如果是相对简单的操作,完全可以自己动手来实现。
SQLAlchemy使用中文参考
SQLAlchemy官方英文文档
Flask-SQLAlchemy 是一个为您的 Flask 应用增加 SQLAlchemy 支持的扩展,致力于简化在 Flask 中 SQLAlchemy 的使用。
参考文档:Flask-SQLAlchemy — Flask-SQLAlchemy 2.0 documentation
安装:
pip3 install flask_sqlalchemy
导入模块时,常见的sqlalchemy配置:
SQLALCHEMY_DATABASE_URI |
用于连接数据的数据库。例如:
|
SQLALCHEMY_BINDS |
一个映射绑定 (bind) 键到 SQLAlchemy 连接 URIs 的字典。 更多的信息请参阅 绑定多个数据库。 |
SQLALCHEMY_ECHO |
如果设置成 True,SQLAlchemy 将会记录所有 发到标准输出(stderr)的语句,这对调试很有帮助。 |
SQLALCHEMY_RECORD_QUERIES |
可以用于显式地禁用或者启用查询记录。查询记录 在调试或者测试模式下自动启用。更多信息请参阅 get_debug_queries() 。 |
SQLALCHEMY_NATIVE_UNICODE |
可以用于显式地禁用支持原生的 unicode。这是 某些数据库适配器必须的(像在 Ubuntu 某些版本上的 PostgreSQL),当使用不合适的指定无编码的数据库 默认值时。 |
SQLALCHEMY_POOL_SIZE |
数据库连接池的大小。默认是数据库引擎的默认值 (通常是 5)。 |
SQLALCHEMY_POOL_TIMEOUT |
指定数据库连接池的超时时间。默认是 10。 |
SQLALCHEMY_POOL_RECYCLE |
自动回收连接的秒数。这对 MySQL 是必须的,默认 情况下 MySQL 会自动移除闲置 8 小时或者以上的连接。 需要注意地是如果使用 MySQL 的话, Flask-SQLAlchemy 会自动地设置这个值为 2 小时。 |
SQLALCHEMY_MAX_OVERFLOW |
控制在连接池达到最大值后可以创建的连接数。当这些额外的 连接回收到连接池后将会被断开和抛弃。 |
SQLALCHEMY_TRACK_MODIFICATIONS |
如果设置成 True (默认情况),Flask-SQLAlchemy 将会追踪对象的修改并且发送信号。这需要额外的内存, 如果不必要的可以禁用它。 |
参考:声明模型 — Flask-SQLAlchemy 2.0 documentation
常见情况下对于只有一个 Flask 应用,所有您需要做的事情就是创建 Flask 应用,选择加载配置接着创建 SQLAlchemy 对象时候把 Flask 应用传递给它作为参数。
一旦创建,这个对象就包含 sqlalchemy
和 sqlalchemy.orm 中的所有函数和助手。此外它还提供一个名为 Model 的类,用于作为声明模型时的 delarative 基类。
我们先来看一个简单的示例,文件内容如下:orm_demo1.py
import pymysql
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from urllib import parse
# 解决报错: ModuleNotFoundError: No module named 'MySQLdb'
pymysql.install_as_MySQLdb()
app = Flask(__name__)
uri = 'mysql://{}:{}@10.xx.xx.xx:3306/{database}'.format(parse.quote_plus("{user}"), parse.quote_plus("{password}"))
app.config['SQLALCHEMY_DATABASE_URI'] = uri
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
# 定义数据模型
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return '' % self.username
用 Column
来定义一列。列名就是您赋值给那个变量的名称。如果您想要在表中使用不同的名称,您可以提供一个想要的列名的字符串作为可选第一个参数。主键用 primary_key=True
标记。可以把多个键标记为主键,此时它们作为复合主键。
列的类型是 Column
的第一个参数。您可以直接提供它们或进一步规定(比如提供一个长度)。下面的类型是最常用的:
Integer | 一个整数 |
String (size) | 有长度限制的字符串 |
Text | 一些较长的 unicode 文本 |
DateTime | 表示为 Python datetime 对象的 时间和日期 |
Float | 存储浮点值 |
Boolean | 存储布尔值 |
PickleType | 存储为一个持久化的 Python 对象 |
LargeBinary | 存储一个任意大的二进制数据 |
为了创建初始数据库,只需要从交互式 Python shell 中导入 db 对象并且调用 SQLAlchemy.create_all() 方法来创建表和数据库:
Ps:当然创建数据库操作也不是说必须的,可以使用数据库中已有的数据表,当数据库的名称不符合默认的映射关系时,可以人工指定表名:__tablename__ = "general-flow-demo"。因为默认类名和数据库中的表的映射关系(驼峰->下划线),比如:class GeneralFlow(db.Model)对应Mysql表中默认的就是general_flow数据表
>>> from orm_demo1 import db
>>> db.create_all()
数据库已经生成,现在来创建一些用户:
from orm_demo1 import User
>>> admin = User('admin', '[email protected]')
>>> guest = User('guest', '[email protected]')
但是它们还没有真正地写入到数据库中,因此让我们来确保它们已经写入到数据库中,单个提交方式:
>>> db.session.add(admin)
>>> db.session.add(guest)
>>> db.session.commit()
可以也可以批量提交:
>>> db.session.add_all([admin, guest])
>>> db.session.commit()
访问数据库中的数据也是十分简单的:
>>> users = User.query.all()
[, ]
>>> admin = User.query.filter_by(username='admin').first()
直接查看数据表User,内容其实是有生成的:
最为常见的关系就是一对多的关系。因为关系在它们建立之前就已经声明,您可以使用字符串来指代还没有创建的类(例如如果 Person 定义了一个到 Article 的关系,而 Article 在文件的后面才会声明)。
关系使用 relationship() 函数表示。然而外键必须用类 sqlalchemy.schema.ForeignKey 来单独声明:
orm_demo2.py
import pymysql
import uuid
import time
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from urllib import parse
from datetime import datetime
# 解决报错: ModuleNotFoundError: No module named 'MySQLdb'
pymysql.install_as_MySQLdb()
app = Flask(__name__)
# app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:tmp/test.db'
uri = 'mysql://{}:{}@10.xx.xx.xx:3306/{database}'.format(parse.quote_plus("{user}"), parse.quote_plus("{password}"))
app.config['SQLALCHEMY_DATABASE_URI'] = uri
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.String(255),primary_key=True)
name = db.Column(db.String(64), unique=True, index=True)
skill = db.relationship('Skill',backref=db.backref('user'))
def __init__(self, **kwargs):
for key in kwargs:
setattr(self, key, kwargs[key])
self.id = str(uuid.uuid1())
def __repr__(self):
return '' % self.name
class Skill(db.Model):
__tablename__ = 'skill'
id = db.Column(db.String(255),primary_key=True)
name = db.Column(db.String(64), unique=True)
user_id = db.Column(db.String(255), db.ForeignKey('user.id'), nullable=False)
def __init__(self, **kwargs):
for key in kwargs:
setattr(self, key, kwargs[key])
self.id = str(uuid.uuid1())
def __repr__(self):
return '' % self.name
# 第一部分: 创建表 + 添加数据
# 创建表
db.create_all()
# User表添加用户
user1 = User(name = 'li')
db.session.add_all([user1])
db.session.commit()
# Skill表添加用户对应的技能
skill1 = Skill(name = 'java',user_id = user1.id)
skill2 = Skill(name = 'python',user_id = user1.id)
db.session.add_all([user1, skill1, skill2])
db.session.commit()
# 查询操作
uli = User.query.filter_by(name = 'li').one()
print(uli.skill)
skillp = Skill.query.filter_by(name = 'python').one()
print(skillp.user)
# 等待20秒
time.sleep(20)
# 第二部分: 增删改查
def query_get():
# 1、查询数据
print("# 1.1 查询所有数据")
users = User.query.all()
for user in users:
print(user.id, user.name)
print("# 1.2 根据主键查询数据")
# get() 或 get_or_404() 方法根据主键来查询数据。get() 方法返回 model 的实例,
# 如果找不到,则返回 None;而 get_or_404() 则返回 Not found 的错误响应。
print(User.query.get(user.id))
query_get()
# 2、筛选/过滤数据
def query_filter(name):
user_id = ""
users = User.query.filter_by(name=name).all()
print("# 2.1 筛选/过滤数据")
# user_id = ""
if len(users) > 0:
for user in users:
user_id = user.id
print(user.id, user.name, user.skill)
else:
print ('No data.')
print("user_id: {}".format(user_id))
return user_id
user_id = query_filter('li')
# 3、更新数据
def update(user_id):
# 使用User表的唯一标识: user_id
user = User.query.get(user_id)
print("# 3.1 更新数据")
user.name = "li_temp"
# 提交修改
db.session.commit()
update(user_id)
query_filter('li_temp')
# 4、删除数据
# 得先删除Skill表中的数据,然后删除User表中的数据, 不然默认设置会报下面的错误: 因为两个表之间通过foreign key关联, 最好都删除干净
# Cannot add or update a child row: a foreign key constraint fails
Skill.query.filter(Skill.user_id==user_id).delete()
print("# 4.1 删除Skill表数据")
# 提交修改
db.session.commit()
user = User.query.get(user_id)
print("# 4.2 删除User表数据")
db.session.delete(user)
# 提交修改
db.session.commit()
query_get()
直接运行,输出结果:
[, ]
# 1.1 查询所有数据
710e4418-ea5a-11ec-9daf-6c92bf7d1ef6 li
# 1.2 根据主键查询数据
# 2.1 筛选/过滤数据
710e4418-ea5a-11ec-9daf-6c92bf7d1ef6 li [, ]
user_id: 710e4418-ea5a-11ec-9daf-6c92bf7d1ef6
# 3.1 更新数据
# 2.1 筛选/过滤数据
710e4418-ea5a-11ec-9daf-6c92bf7d1ef6 li_temp [, ]
user_id: 710e4418-ea5a-11ec-9daf-6c92bf7d1ef6
# 4.1 删除Skill表数据
# 4.2 删除User表数据
# 1.1 查询所有数据
上面的代码主要分为2部分:
Flask-sqlalchemy增删改查之(删除数据)
两张表关联的方式简单说明几点:
为了方便对比数据,程序中间等待了20秒,可以人工查看删除操作前后的数据表数据情况:
删除操作前:
删除操作后:
增删改查参考:选择(Select),插入(Insert), 删除(Delete) — Flask-SQLAlchemy 2.0 documentation