Flask-SQLAlchemy 數據庫操作
關于Flask數據庫
Flask中沒有指定使用的數據庫,可以自由選擇不管你是使用關系型數據庫,還是非關系型數據庫都可以,不像django提供了orm 數據庫抽象層,可以直接采用對象的方式操作數據庫。但是為了開發效率,在開發Flask項目中我們一般會選擇 SQLALchemy 來操作數據庫,類似于Django的ORM。SQLALchemy實際上是對數據庫的抽象,讓開發者不直接使用sql語句做開發,而是通過Python對象來操作數據庫,在舍棄一些性能開銷的同時,換來的是開發效率的較大提升。
SQLAlchemy是一個關系型數據庫框架,它提供了高層的ORM和底層的原生數據庫的操作。為了簡化配置和操作,我們使 Flask-SQLAlchemy,這個 Flask 擴展封裝了 SQLAlchemy 框架。
安裝 Flask-SQLAlchemy
pip install Flask-SQLAlchemy
要連接mysql數據庫還需要安裝flask-mysqldb
pip install flask-mysqldb
如果安裝報下面錯誤
Command "python setup.py egg_info" failed with error code 1
請先安裝執行下面這條命令:(Linux)
sudo apt-get install mysql-server libmysqld-dev
示例:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
?
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:xxxxxx@127.0.0.1/flask01'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True ?# 追蹤對象的修改并且發送信號
db = SQLAlchemy(app)
?
class User(db.Model):# SQLAlchemy 需要手動執行主鍵列,第一個參數是 字段類型,第二個參數是約束條件id = db.Column(db.Integer,primary_key=True)name = db.Column(db.String(20),unique=True)
?
if __name__ == '__main__':with app.app_context(): ?# 添加應用上下文# 創建所有表db.create_all()
1. 數據庫連接配置
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
?
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/py'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True ?# 追蹤對象的修改并且發送信號
db = SQLAlchemy(app)
配置鍵說明:
配置鍵 | 說明 |
---|---|
SQLALCHEMY_DATABASE_URI | 設置連接數據庫<br>格式:mysql://username:password@server/db |
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 將 會追蹤對象的修改并且發送信號。這需要額外的內 存,如果不必要的可以禁用它。 |
SQLALCHEMY_COMMIT_ON_TEARDOWN | 每次請求結束后會自動提交數據庫中的改動 |
數據庫URI格式:
數據庫 | URI格式 |
---|---|
MySQL | mysql://username:password@hostname/database |
Postgres | postgresql://username:password@hostname/database |
SQLite (Unix) | sqlite:////python/data/database |
SQLite (Windows) | sqlite:///c:/db/data/database |
Oracle | oracle://scott:tiger@127.0.1:1521/sidname |
字段說明: username:登錄數據庫的用戶名 password:登錄數據庫的密碼 hostname:服務器主機 ip,可以是本地主機(localhost)也可以是遠程服務器 database:表示要使用的數據庫
2. 模型類定義
字段類型:
類型名 | Python中類型 | 說明 |
---|---|---|
Integer | int | 普通整數,一般是32位 |
SmallInteger | int | 小整數 |
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 | 二進制文件 |
約束條件:
約束 | 說明 |
---|---|
primary_key | 主鍵 |
unique | 唯一約束,True不允許重復 |
index | 索引,如果為True,為這列創建索引,提高查詢效率 |
nullable | 空值,如果為True,允許有空值,如果為False,不允許有空值 |
default | 默認值 |
關系類型:
關系 | 說明 |
---|---|
One To Many(一對多) | 表示一對多的關系時,在子表類中通過 foreign key (外鍵)引用父表類,在父表類中通過 relationship() 方法來引用了表的類 |
One To One(一對一) | 一對一是兩張表之間本質上的雙向關系。只需要在一對多關系基礎上的父表中使用 uselist=False 參數來表示 |
Many To Many(多對多) | 多對多關系會在兩個類之間增加一個關聯的表,連個關系表中使用 relationship() 方法中通過 secondary 來引用關聯表,關聯表通過 MetaData 對象與聲明基類關聯,使用ForeignKey 連接接來定位到遠程的表 |
3. 關系模型
一對多關系
class Father(db.Model):"""創建一個父表模型類"""# 表名__tablename__ = 'father'# id主鍵列,整數類型,自增id = db.Column(db.Integer, primary_key=True)# name,可變長字符串類型name = db.Column(db.String(20))# 關系字段,不是數據庫中真實存在的字段,而是為了方便查詢添加的屬性son = db.relationship('Son', backref='father')
?
class Son(db.Model):"""創建一個子表模型類"""# 表名__tablename__ = 'son'# id主鍵列,整數類型,自增id = db.Column(db.Integer, primary_key=True)# name,可變長字符串類型name = db.Column(db.String(20))# 外鍵,需要指定字段類型,以及關聯那個表的那個字段father_id = db.Column(db.Integer, db.ForeignKey('father.id'))
一對一關系
class Father(db.Model):"""創建一個父類模型類"""# 表名__tablename__ = 'father'# id主鍵列,整數類型,自增id = db.Column(db.Integer, primary_key=True)# name,可變長字符串類型name = db.Column(db.String(20))# 關系字段,不是數據庫中真實存在的字段,而是為了方便查詢添加的屬性son = db.relationship('Son', backref='father', uselist=False)
?
class Son(db.Model):"""創建一個子表模型類"""# 表名__tablename__ = 'son'# id主鍵列,整數類型,自增id = db.Column(db.Integer, primary_key=True)# name,可變長字符串類型name = db.Column(db.String(20))# 外鍵,需要指定字段類型,以及關聯那個表的那個字段father_id = db.Column(db.Integer, db.ForeignKey('father.id'))
多對多關系
# 多對多關系中的兩個表之間的一個關聯表
association_table = db.Table('association', db.metadata,db.Column('author_id', db.Integer, db.ForeignKey('author.id')),db.Column('book_id', db.Integer, db.ForeignKey('book.id'))
)
?
class Author(db.Model):"""創建作者模型類"""# 指定表名,如果沒有指定將默認使用模型類的名稱__tablename__ = 'author'# id 類型是整數 主鍵列id = db.Column(db.Integer, primary_key=True)# name 類型是可變長字符串,唯一name = db.Column(db.String(20), unique=True)# 關聯中間表,不是數據庫中真實存在的字段,而是為了方便查詢添加的屬性book = db.relationship('Book', back_populates='author',secondary=association_table)
?
class Book(db.Model):"""創建書模型類"""# 指定表名,如果不指定將使用模型類名稱作為表名__tablename__ = 'book'id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(20))# 關聯中間表,不是數據庫中真實存在的字段,而是為了方便查詢添加的屬性author = db.relationship("Author", secondary=association_table,back_populates="book")
4. 數據操作
創建表:
if __name__ == '__main__':# 刪除所有表,注意這條是危險命令,會將模型類對應數據庫中的表物理刪除。在實際生產環境下勿用。db.drop_all()# 創建所有表db.create_all()
添加數據:
# 創建一個 father對象
f = Father(name='小頭爸爸')
# 將數據添加到會話
db.session.add(f)
# 提交會話數據
db.session.commit()
?
# 創建兩個Son對象,這里利用到father對象id,需要先將Father對象提交才能拿到id
s = Son(name='大頭兒子', father_id=f.id)
s1 = Son(name='中頭兒子', father_id=f.id)
# 同時添加多個數據,接收一個列表做參數
db.session.add_all([s, s1])
# 提交會話數據
db.session.commit()
事務管理:
# 創建一個 mother
m = Father(name='圍裙媽媽')
db.session.add(m)
?
# 創建了一個媽媽,但是我們這里是Father類,需要回滾會話
db.session.rollback()
刪除數據:
# 刪除演員表中的'吳孟達'
# 將數據查詢出來
actor = Cast.query.filter(Cast.name=='吳孟達').first()
# 刪除對象
db.session.delete(actor)
# 提交到數據庫
db.session.commit()
更新數據:
# 第一種方式:查詢后修改
# 先將'梁小龍'查詢出來
actor = Cast.query.filter(Cast.name=='梁小龍').first()
# 將'梁小龍'的名字改成'火云邪神'
actor.name = '火云邪神'
db.session.add(actor)
db.session.commit()
?
# 第二種方式:update方式
# 將'朱茵'修改成'紫霞仙子'
Cast.query.filter(Cast.name=='朱茵').update({'name':'紫霞仙子'})
db.session.commit()
5. 數據查詢
基本查詢方法:
方法 | 作用 |
---|---|
all() | 返回數據庫中所有數據,列表形式返回 |
first() | 返回查詢的第一個結果,如果未查到,返回None |
first_or_404() | 返回查詢的第一個結果,如果未查到,返回404 |
get() | 返回指定主鍵對應的行,如不存在,返回None |
get_or_404() | 返回指定主鍵對應的行,如不存在,返回404 |
count() | 返回查詢結果的數量 |
paginate() | 返回一個Paginate對象,它包含指定范圍內的結果 |
查詢過濾器:
過濾器 | 說明 |
---|---|
filter() | 把過濾器添加到原查詢上,返回一個新查詢 |
filter_by() | 把等值過濾器添加到原查詢上,返回一個新查詢 |
limit | 使用指定的值限定原查詢返回的結果 |
offset() | 偏移原查詢返回的結果,返回一個新查詢 |
order_by() | 根據指定條件對原查詢結果進行排序,返回一個新查詢 |
group_by() | 根據指定條件對原查詢結果進行分組,返回一個新查詢 |
查詢示例:
# filter_by 精確匹配查詢,條件只能是本表的字段
Cast.query.filter_by(name='周星馳').all()
?
# first()返回查詢到的第一個對象
Cast.query.first()
?
# all()返回查詢到的所有對象
Cast.query.all()
?
# filter 模糊查詢
Cast.query.filter(Cast.name.startswith('周'))
Cast.query.filter(Cast.name.endswith('龍'))
Cast.query.filter(Cast.name=='周星馳').all()
?
# like 正則模糊匹配
Cast.query.filter(Cast.name.like('周%%'))
邏輯運算查詢:
from sqlalchemy import and_, or_, not_
?
# 查詢名字為'周星馳'并且movie_id為2的演員數據
cast.query.filter(and_(cast.name=='周星馳', cast.movie_id==2)).all()
?
# 查詢名字為'周星馳'或者id為2的演員數據
cast.query.filter(or_(cast.name=='周星馳', cast.movie_id==2)).all()
?
# 查詢名字不為'周星馳'的演員
cast.query.filter(not_(cast.name=='周星馳')).all()
關聯查詢:
# 查詢名字為朱茵的演員,并且查出她出演的電影
# 查詢出演員'朱茵'
actor = Cast.query.filter(Cast.name=='朱茵').all()
# 直接通過 relationship 中定義的 backref 屬性可以直接查詢出演員對應的電影
movie = actor[0].Movie
# 通過電影直接查詢出所有演員
actors = movie.cast
添加repr方法:
def __repr__(self):return "Father: %s" % self.name
6. 完整示例
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
?
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@192.168.20.233:3306/py'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
?
class Movie(db.Model):"""創建一個電影模型類"""# 表名__tablename__ = 'movie'# id主鍵列,整數類型,自增id = db.Column(db.Integer, primary_key=True)# name,可變長字符串類型name = db.Column(db.String(20))# 關系字段,不是數據庫中真實存在的字段,而是為了方便查詢添加的屬性cast = db.relationship('Cast', backref='Movie')def __repr__(self):return "Movie: %s " % self.name
?
class Cast(db.Model):"""演員模型類"""# id主鍵列,整數類型,自增id = db.Column(db.Integer, primary_key=True)# name,可變長字符串類型name = db.Column(db.String(20))# 外鍵關聯idmovie_id = db.Column(db.Integer, db.ForeignKey('movie.id'))def __repr__(self):return "Cast: %s " % self.name
?
if __name__ == '__main__':# 刪除所有表db.drop_all()# 創建所有表db.create_all()# 創建電影數據m1 = Movie(name='大話西游')m2 = Movie(name='功夫')db.session.add_all([m1, m2])db.session.commit()# 創建演員數據cast_list = []for i in ['周星馳','朱茵','吳孟達','莫文蔚']:c = Cast(name=i, movie_id=m1.id)cast_list.append(c)for i in ['周星馳','梁小龍','元華']:c = Cast(name=i, movie_id=m2.id)cast_list.append(c)db.session.add_all(cast_list)db.session.commit()