Flask 操作Mysql數據庫 - flask-sqlalchemy擴展
官網文檔
flask-sqlalchemy.palletsprojects.com/en/master/q…
數據庫的設置
Web應用中普遍使用的是關系模型的數據庫,關系型數據庫把所有的數據都存儲在表中,表用來給應用的實體建模,表的列數是固定的,行數是可變的。它使用結構化的查詢語言。關系型數據庫的列定義了表中表示的實體的數據屬性。比如:商品表里有name、price、number等。 Flask本身不限定數據庫的選擇,你可以選擇SQL或NOSQL的任何一種。也可以選擇更方便的SQLALchemy,類似於Django的ORM。SQLALchemy實際上是對數據庫的抽象,讓開發者不用直接和SQL語句打交道,而是通過Python對象來操作數據庫,在舍棄一些性能開銷的同時,換來的是開發效率的較大提升。
SQLAlchemy是一個關系型數據庫框架,它提供了高層的ORM和底層的原生數據庫的操作。flask-sqlalchemy是一個簡化了SQLAlchemy操作的flask擴展。
下面使用mysql作為示例進行說明。
創建mysql數據庫
1.登錄數據庫
mysql -u root -p password
復制代碼
2.創建數據庫,並設定編碼
create database <數據庫名> charset=utf8;
復制代碼
3.顯示所有數據庫
show databases;
復制代碼
4.執行如下
mysql> create database flask_ex charset=utf8;
Query OK, 1 row affected (0.06 sec)
復制代碼
安裝flask-sqlalchemy的擴展
pip install -U Flask-SQLAlchemy
復制代碼
python2:要連接mysql數據庫,仍需要安裝flask-mysqldb
pip install flask-mysqldb
復制代碼
python3:要連接mysql數據庫,仍需要安裝pymysql
pip install pymysql
復制代碼
本篇章內容以python3作為開講。
使用Flask-SQLAlchemy連接mysql數據庫
使用Flask-SQLAlchemy擴展操作數據庫,首先需要建立數據庫連接。數據庫連接通過URL指定,而且程序使用的數據庫必須保存到Flask配置對象的SQLALCHEMY_DATABASE_URI鍵中。
對比下Django和Flask中的數據庫設置:
Django的數據庫設置:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 修改后端數據庫使用mysql
'NAME': 'mydb', # 設置訪問數據庫名稱
'USER': 'root', # 訪問訪問mysql用戶名
'PASSWORD': 'password', # 設置訪問密碼
'HOST': 'localhost', # 設置訪問ip地址
'PORT': 3306, # 設置訪問端口號
}
}
復制代碼
Flask的數據庫設置:
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/flask_ex'
復制代碼
常用的SQLAlchemy字段類型
上面看完了如何設置連接數據庫,那么來看看,使用SQLAlchemy創建數據模型的時候,基本的字段類型如下:
類型名 | python中類型 | 說明 |
---|---|---|
Integer | int | 普通整數,一般是32位 |
SmallInteger | int | 取值范圍小的整數,一般是16位 |
BigInteger | int或long | 不限制精度的整數 |
Float | float | 浮點數 |
Numeric | decimal.Decimal | 普通整數,一般是32位 |
String | str | 變長字符串 |
Text | str | 變長字符串,對較長或不限長度的字符串做了優化 |
Unicode | unicode | 變長Unicode字符串 |
UnicodeText | unicode | 變長Unicode字符串,對較長或不限長度的字符串做了優化 |
Boolean | bool | 布爾值 |
Date | datetime.date | 時間 |
Time | datetime.datetime | 日期和時間 |
LargeBinary | str | 二進制文件 |
常用的SQLAlchemy列選項
選項名 | 說明 |
---|---|
primary_key | 如果為True,代表表的主鍵 |
unique | 如果為True,代表這列不允許出現重復的值 |
index | 如果為True,為這列創建索引,提高查詢效率 |
nullable | 如果為True,允許有空值,如果為False,不允許有空值 |
default | 為這列定義默認值 |
常用的SQLAlchemy關系選項
選項名 | 說明 |
---|---|
backref | 在關系的另一模型中添加反向引用 |
primary join | 明確指定兩個模型之間使用的聯結條件 |
uselist | 如果為False,不使用列表,而使用標量值 |
order_by | 指定關系中記錄的排序方式 |
secondary | 指定多對多中記錄的排序方式 |
secondary join | 在SQLAlchemy中無法自行決定時,指定多對多關系中的二級聯結條件 |
上面這些有很多基本選項的說明,下面來進行數據庫的基本增刪改等操作來加強理解。
數據庫基本操作
在Flask-SQLAlchemy中,插入、修改、刪除操作,均由數據庫會話管理。會話用db.session表示。在准備把數據寫入數據庫前,要先將數據添加到會話中然后調用commit()方法提交會話。
數據庫會話是為了保證數據的一致性,避免因部分更新導致數據不一致。提交操作把會話對象全部寫入數據庫,如果寫入過程發生錯誤,整個會話都會失效。
數據庫會話也可以回滾,通過db.session.rollback()方法,實現會話提交數據前的狀態。
在Flask-SQLAlchemy中,查詢操作是通過query對象操作數據。最基本的查詢是返回表中所有數據,可以通過過濾器進行更精確的數據庫查詢。
下面先來創建兩個表的數據模型:用戶表和角色表。
在視圖函數中定義模型類
看完了上面那么多的概念說明,下面來看看如何創建數據模型以及創建數據表,如下:
1.在腳本15_SQLAlchemy.py編寫創建User和Role數據模型
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import pymysql
pymysql.install_as_MySQLdb()
app = Flask(__name__)
class Config(object):
"""配置參數"""
# 設置連接數據庫的URL
user = 'root'
password = '********'
database = 'flask_ex'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://%s:%s@127.0.0.1:3306/%s' % (user,password,database)
# 設置sqlalchemy自動更跟蹤數據庫
SQLALCHEMY_TRACK_MODIFICATIONS = True
# 查詢時會顯示原始SQL語句
app.config['SQLALCHEMY_ECHO'] = True
# 禁止自動提交數據處理
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = False
# 讀取配置
app.config.from_object(Config)
# 創建數據庫sqlalchemy工具對象
db = SQLAlchemy(app)
class Role(db.Model):
# 定義表名
__tablename__ = 'roles'
# 定義字段
id = db.Column(db.Integer, primary_key=True,autoincrement=True)
name = db.Column(db.String(64), unique=True)
users = db.relationship('User',backref='role') # 反推與role關聯的多個User模型對象
class User(db.Model):
# 定義表名
__tablename__ = 'users'
# 定義字段
id = db.Column(db.Integer, primary_key=True,autoincrement=True)
name = db.Column(db.String(64), unique=True, index=True)
email = db.Column(db.String(64),unique=True)
pswd = db.Column(db.String(64))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) # 設置外鍵
if __name__ == '__main__':
# 刪除所有表
db.drop_all()
# 創建所有表
db.create_all()
復制代碼
- 執行腳本,創建數據庫
python3 15_SQLAlchemy.py
復制代碼
3.在mysql查看已經創建的表結構
mysql> show tables;
+--------------------+
| Tables_in_flask_ex |
+--------------------+
| roles |
| users |
+--------------------+
2 rows in set (0.00 sec)
mysql>
mysql> desc users;
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(64) | YES | UNI | NULL | |
| email | varchar(64) | YES | UNI | NULL | |
| pswd | varchar(64) | YES | | NULL | |
| role_id | int(11) | YES | MUL | NULL | |
+---------+-------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
mysql>
mysql> desc roles;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(64) | YES | UNI | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
mysql>
復制代碼
創建好了數據表之后,下面來看看如何執行數據的增刪查改的。
常用的SQLAlchemy查詢過濾器
過濾器 | 說明 |
---|---|
filter() | 把過濾器添加到原查詢上,返回一個新查詢 |
filter_by() | 把等值過濾器添加到原查詢上,返回一個新查詢 |
limit | 使用指定的值限定原查詢返回的結果 |
offset() | 偏移原查詢返回的結果,返回一個新查詢 |
order_by() | 根據指定條件對原查詢結果進行排序,返回一個新查詢 |
group_by() | 根據指定條件對原查詢結果進行分組,返回一個新查詢 |
常用的SQLAlchemy查詢執行器
方法 | 說明 |
---|---|
all() | 以列表形式返回查詢的所有結果 |
first() | 返回查詢的第一個結果,如果未查到,返回None |
first_or_404() | 返回查詢的第一個結果,如果未查到,返回404 |
get() | 返回指定主鍵對應的行,如不存在,返回None |
get_or_404() | 返回指定主鍵對應的行,如不存在,返回404 |
count() | 返回查詢結果的數量 |
paginate() | 返回一個Paginate對象,它包含指定范圍內的結果 |
創建表:
db.create_all()
復制代碼
刪除表
db.drop_all()
復制代碼
每次插入單條數據
if __name__ == '__main__':
# 插入一條角色數據
role1 = Role(name='admin')
db.session.add(role1)
db.session.commit()
# 再次插入一條數據
role2 = Role(name='user')
db.session.add(role2)
db.session.commit()
復制代碼
執行腳本:
python3 15_SQLAlchemy.py
復制代碼
在mysql中查看插入的數據,如下:
mysql> select * from roles \G
*************************** 1. row ***************************
id: 1
name: admin
*************************** 2. row ***************************
id: 2
name: user
2 rows in set (0.00 sec)
復制代碼
一次插入多條數據
# 一次性插入多條數據
user1 = User(name='wang',email='wang@163.com',pswd='123456',role_id=role1.id)
user2 = User(name='zhang',email='zhang@189.com',pswd='201512',role_id=role2.id)
user3 = User(name='chen',email='chen@126.com',pswd='987654',role_id=role2.id)
user4 = User(name='zhou',email='zhou@163.com',pswd='456789',role_id=role1.id)
db.session.add_all([user1,user2,user3,user4])
db.session.commit()
復制代碼
執行插入數據,如下:
python3 15_SQLAlchemy.py
復制代碼
在mysql中查詢插入的數據如下:
mysql> select * from users \G
*************************** 1. row ***************************
id: 1
name: wang
email: wang@163.com
pswd: 123456
role_id: 1
*************************** 2. row ***************************
id: 2
name: zhang
email: zhang@189.com
pswd: 201512
role_id: 2
*************************** 3. row ***************************
id: 3
name: chen
email: chen@126.com
pswd: 987654
role_id: 2
*************************** 4. row ***************************
id: 4
name: zhou
email: zhou@163.com
pswd: 456789
role_id: 1
4 rows in set (0.00 sec)
mysql>
復制代碼
雖然這里在python中看上去是一次性插入多條數據,其實在mysql也是執行多行插入的語句,通過mysql的日志可以看到如下:
2019-11-23T16:48:56.984459Z 9061 Query INSERT INTO users (name, email, pswd, role_id) VALUES ('wang', 'wang@163.com', '123456', 1)
2019-11-23T16:48:56.997132Z 9061 Query INSERT INTO users (name, email, pswd, role_id) VALUES ('zhang', 'zhang@189.com', '201512', 2)
2019-11-23T16:48:57.010175Z 9061 Query INSERT INTO users (name, email, pswd, role_id) VALUES ('chen', 'chen@126.com', '987654', 2)
2019-11-23T16:48:57.024134Z 9061 Query INSERT INTO users (name, email, pswd, role_id) VALUES ('zhou', 'zhou@163.com', '456789', 1)
復制代碼
實際上並沒有將多個values合並到一個insert語句,依然是多個insert語句逐個插入的。
查詢:filter_by精確查詢
返回名字等於wang的所有user
User.query.filter_by(name='wang').all()
復制代碼
在交互模型執行如下:
>python3 db_demo.py shell
In [1]: from db_demo import User
In [2]: User.query.filter_by(name='wang').all()
Out[2]: [<User 1>]
In [3]:
復制代碼
first()返回查詢到的第一個對象
User.query.first()
復制代碼
執行如下:
In [3]: User.query.first()
Out[3]: <User 1>
復制代碼
all()返回查詢到的所有對象
User.query.all()
復制代碼
執行如下:
In [4]: User.query.all()
Out[4]: [<User 1>, <User 2>, <User 3>, <User 4>]
In [5]:
復制代碼
filter模糊查詢,返回名字結尾字符為g的所有數據。
User.query.filter(User.name.endswith('g')).all()
復制代碼
執行如下:
In [5]: User.query.filter(User.name.endswith('g')).all()
Out[5]: [<User 1>, <User 2>]
復制代碼
get(),參數為主鍵,如果主鍵不存在沒有返回內容
User.query.get()
復制代碼
執行如下:
In [6]: User.query.get(2)
Out[6]: <User 2>
In [7]: user2 = User.query.get(2)
In [8]: user2.name
Out[8]: 'zhang'
復制代碼
邏輯非,返回名字不等於wang的所有數據。
User.query.filter(User.name!='wang').all()
復制代碼
執行如下:
In [9]: User.query.filter(User.name!='wang').all()
Out[9]: [<User 2>, <User 3>, <User 4>]
復制代碼
邏輯與,需要導入and,返回and()條件滿足的所有數據。
from sqlalchemy import and_
User.query.filter(and_(User.name!='wang',User.email.endswith('163.com'))).all()
復制代碼
執行如下:
In [10]: from sqlalchemy import and_
In [15]: users = User.query.filter(and_(User.name!='wang',User.email.endswith('163.com'))).all()
In [16]: for user in users:
...: print(user.email)
...:
zhou@163.com
復制代碼
邏輯或,需要導入or_
from sqlalchemy import or_
User.query.filter(or_(User.name!='wang',User.email.endswith('163.com'))).all()
復制代碼
執行如下:
In [17]: from sqlalchemy import or_
In [18]: users = User.query.filter(or_(User.name!='wang',User.email.endswith('163.com'))).all()
In [19]: for user in users:
...: print(user.name, user.email)
...:
wang wang@163.com
zhang zhang@189.com
chen chen@126.com
zhou zhou@163.com
In [20]:
復制代碼
not_ 相當於取反
from sqlalchemy import not_
User.query.filter(not_(User.name=='chen')).all()
復制代碼
執行如下:
In [22]: from sqlalchemy import not_
In [25]: users = User.query.filter(not_(User.name=='chen')).all()
In [26]: for user in users:
...: print(user.name, user.email)
...:
wang wang@163.com
zhang zhang@189.com
zhou zhou@163.com
復制代碼
查詢數據后刪除
user = User.query.first()
db.session.delete(user)
db.session.commit()
User.query.all()
復制代碼
執行如下:
(venv) $ >python3 db_demo.py shell
In [1]: from db_demo import User
In [3]: user = User.query.first()
In [5]: from db_demo import db
In [6]: db.session.delete(user)
In [7]: db.session.commit()
In [8]: User.query.all()
Out[8]: [<User 2>, <User 3>, <User 4>]
復制代碼
更新數據
user = User.query.first()
user.name = 'dong'
db.session.commit()
User.query.first()
復制代碼
執行如下:
In [1]: from db_demo import User
In [5]: from db_demo import db
In [9]: user = User.query.first()
In [10]: user
Out[10]: <User 2>
In [11]: user.name
Out[11]: 'zhang'
In [12]: user.name = 'dong'
In [13]: db.session.commit()
In [14]: user = User.query.first()
In [15]: user.name
Out[15]: 'dong'
復制代碼
使用update
User.query.filter_by(name='zhang').update({'name':'li'})
復制代碼
執行如下:
In [21]: User.query.filter_by(name='dong').update({'name':'li'})
Out[21]: 0
In [22]: User.query.get(2)
Out[22]: <User 2>
In [23]: user = User.query.get(2)
In [24]: user.name
Out[24]: 'li'
復制代碼
關聯查詢示例:角色和用戶的關系是一對多的關系,一個角色可以有多個用戶,一個用戶只能屬於一個角色。
關聯查詢角色的所有用戶:
#查詢roles表id為1的角色
role1 = Role.query.get(1)
#查詢該角色的所有用戶
role1.users
復制代碼
執行如下:
In [25]: from db_demo import Role
In [26]: role1 = Role.query.get(1)
In [27]: role1.users
Out[27]: [<User 4>]
In [28]: role2 = Role.query.get(2)
In [29]: role2.users
Out[29]: [<User 2>, <User 3>]
復制代碼
關聯查詢用戶所屬角色:
#查詢users表id為3的用戶
user1 = User.query.get(3)
#查詢用戶屬於什么角色
user1.role
復制代碼
執行如下:
In [30]: user1 = User.query.get(3)
In [31]: user1.role
Out[31]: <Role 2>