SQLAlchemy是什么
為什么使用orm
優(yōu)點文章來源:http://www.zghlxwxcb.cn/news/detail-548878.html
- 有語法提示, 省去自己拼寫SQL,保證SQL語法的正確性
- orm提供方言功能(dialect, 可以轉(zhuǎn)換為多種數(shù)據(jù)庫的語法), 減少學(xué)習(xí)成本
- 防止sql注入攻擊
- 搭配數(shù)據(jù)遷移, 更新數(shù)據(jù)庫方便
- 面向?qū)ο? 可讀性強, 開發(fā)效率高
缺點
- 需要語法轉(zhuǎn)換, 效率比原生sql低
- 復(fù)雜的查詢往往語法比較復(fù)雜 (可以使用原生sql替換)
定義
SQLAlchemy是一個基于Python實現(xiàn)的ORM框架。該框架建立在 DB API之上,使用關(guān)系對象映射進(jìn)行數(shù)據(jù)庫操作,簡言之便是:將類和對象轉(zhuǎn)換成SQL,然后使用數(shù)據(jù)API執(zhí)行SQL并獲取執(zhí)行結(jié)果。
安裝
pip3 install sqlalchemy
組成部分
- Engine,框架的引擎
- Connection Pooling ,數(shù)據(jù)庫連接池
- Dialect,選擇連接數(shù)據(jù)庫的DB API種類
- Schema/Types,架構(gòu)和類型
- SQL Exprression Language,SQL表達(dá)式語言
SQLAlchemy本身無法操作數(shù)據(jù)庫,其必須以來pymsql等第三方插件
MySQL-Python
mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
pymysql
mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
MySQL-Connector
mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
cx_Oracle
oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
更多:http://docs.sqlalchemy.org/en/latest/dialects/index.html
SQLAlchemy的使用
原生sql
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
from urllib import parse
import threading
user = "root"
password = "xxx@000"
pwd = parse.quote_plus(password) # 解決密碼中含@符導(dǎo)致報錯
host = "127.0.0.1:"
# 第一步: 創(chuàng)建engine
engine = create_engine(
f"mysql+pymysql://{user}:{pwd}@{host}3306/test1?charset=utf8",
max_overflow=0, # 超過連接池大小外最多創(chuàng)建的連接
pool_size=5, # 連接池大小
pool_timeout=30, # 池中沒有線程最多等待的時間,否則報錯
pool_recycle=-1 # 多久之后對線程池中的線程進(jìn)行一次連接的回收(重置)
)
# 第二步:使用
def task():
conn = engine.raw_connection() # 從連接池中取一個連接
cursor = conn.cursor()
sql = "select * from signer"
cursor.execute(sql)
print(cursor.fetchall())
if __name__ == '__main__':
for i in range(20):
t = threading.Thread(target=task)
t.start()
使用orm映射數(shù)據(jù)表
import datetime
from sqlalchemy.ext.declarative import declarative_base
from model import engine # 用的簡單使用里面的engine
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
Base = declarative_base() # 基類
# 表模型
class Users(Base):
__tablename__ = 'users' # 數(shù)據(jù)庫表名稱, 必須寫
id = Column(Integer, primary_key=True) # id 主鍵
name = Column(String(32), index=True, nullable=False) # name列,索引,不可為空
email = Column(String(32), unique=True)
# datetime.datetime.now不能加括號,加了括號,以后永遠(yuǎn)是當(dāng)前時間
ctime = Column(DateTime, default=datetime.datetime.now)
extra = Column(Text, nullable=True)
__table_args__ = ( # 可選
UniqueConstraint('id', 'name', name='uix_id_name'), # 聯(lián)合唯一
Index('ix_id_name', 'name', 'email'), # 索引
)
def init_db():
"""
根據(jù)類創(chuàng)建繼承base類的表
:return:
"""
Base.metadata.create_all(engine)
def drop_db():
"""
根據(jù)類刪除繼承base類的表
:return:
"""
Base.metadata.drop_all(engine)
if __name__ == '__main__':
# sqlalchemy 只支持 創(chuàng)建和刪除表,不支持修改表(django orm支持)。sqlalchemy 需要借助第三方實現(xiàn)
init_db() # 創(chuàng)建表
# drop_db() # 刪除表
外鍵關(guān)系
一對多(ForeignKey)
class Hobby(Base):
__tablename__ = 'hobby'
id = Column(Integer, primary_key=True)
caption = Column(String(50), default='籃球')
class Person(Base):
__tablename__ = 'person'
nid = Column(Integer, primary_key=True)
name = Column(String(32), index=True, nullable=True)
# hobby指的是tablename而不是類名
hobby_id = Column(Integer, ForeignKey("hobby.id"))
# 跟數(shù)據(jù)庫無關(guān),不會新增字段,只用于快速鏈表操作
# 類名,backref用于反向查詢 參數(shù) uselist=False , 設(shè)置就變成了一對一,其他和一對多一樣
hobby=relationship('Hobby',backref='pers')
# hobby=relationship('Hobby',backref='pers', uselist=False) # 一對一關(guān)系
多對多
class Boy2Girl(Base):
__tablename__ = 'boy2girl'
id = Column(Integer, primary_key=True, autoincrement=True)
girl_id = Column(Integer, ForeignKey('girl.id'))
boy_id = Column(Integer, ForeignKey('boy.id'))
class Girl(Base):
__tablename__ = 'girl'
id = Column(Integer, primary_key=True)
name = Column(String(64), unique=True, nullable=False)
class Boy(Base):
__tablename__ = 'boy'
id = Column(Integer, primary_key=True, autoincrement=True)
hostname = Column(String(64), unique=True, nullable=False)
# 與生成表結(jié)構(gòu)無關(guān),僅用于查詢方便,放在哪個單表中都可以
servers = relationship('Girl', secondary='boy2girl', backref='boys')
'''
girl_id = Column(Integer, ForeignKey("hobby.id", ondelete='SET NULL')) # 一般用SET NULL
外鍵約束
1. RESTRICT:若子表中有父表對應(yīng)的關(guān)聯(lián)數(shù)據(jù),刪除父表對應(yīng)數(shù)據(jù),會阻止刪除。默認(rèn)項
2. NO ACTION:在MySQL中,同RESTRICT。
3. CASCADE:級聯(lián)刪除。
4. SET NULL:父表對應(yīng)數(shù)據(jù)被刪除,子表對應(yīng)數(shù)據(jù)項會設(shè)置為NULL。
'''
擴(kuò)充:在 django 中,外鍵管理有個參數(shù)
db_contraint=False
用來在邏輯上關(guān)聯(lián)表,但實體不建立約束。同樣在SQLAlchemy
中也可以通過配值relationship
參數(shù)來實現(xiàn)同樣的效果
class Boy2Girl(Base):
__tablename__ = 'boy2girl'
id = Column(Integer, primary_key=True, autoincrement=True)
girl_id = Column(Integer) # 不用ForeignKey
boy_id = Column(Integer)
gitl = db.relationship(
"Girl",
# uselist=False, # 一對一設(shè)置
backref=backref("to_course", uselist=False), # backref用于反向查詢 uselist 作用同上
lazy="subquery", # 懶加載 用來指定sqlalchemy 什么時候加載數(shù)據(jù)
primaryjoin="Girl.id==Boy2Girl.girl_id", # 指定對應(yīng)關(guān)系
foreign_keys="Boy2Girl.girl_id" # 指定表的外鍵字段
)
'''
lazy 可選值
select:就是訪問到屬性的時候,就會全部加載該屬性的數(shù)據(jù) 默認(rèn)值
joined:對關(guān)聯(lián)的兩個表使用聯(lián)接
subquery:與joined類似,但使用子子查詢
dynamic:不加載記錄,但提供加載記錄的查詢,也就是生成query對象
'''
使用orm操作記錄
簡單表操作
from sqlalchemy.orm import sessionmaker
from model import engine
from db_model import Users
# 定義一個 session, 以后操作數(shù)據(jù)都用 session 來執(zhí)行
Session = sessionmaker(bind=engine)
session = Session()
# 創(chuàng)建User對象
usr = Users(name="yxh", email="152@11.com", extra="xxx")
# 通過 user對象 添加到session中
session.add(usr)
# 提交,才會刷新到數(shù)據(jù)庫中,不提交不會執(zhí)行
session.commit()
基于scoped_session實現(xiàn)線程安全
session 如果是一個全局對象。那么在多線程的情況下,并發(fā)使用同一個變量 session 是不安全的,解決方案如下:
-
將session定義在局部,每一個view函數(shù)都定義一個session。 代碼冗余,不推薦
-
基于scoped_session 實現(xiàn)線程安全。原理同 request對象,g對象一致。也是基于local,給每一個線程創(chuàng)造一個session
from sqlalchemy.orm import sessionmaker, scoped_session
from model import engine
from db_model import Users
# 定義一個 session
Session = sessionmaker(bind=engine)
# session = Session()
session = scoped_session(Session) # 后續(xù)使用這個session就是線程安全的
# 創(chuàng)建User對象
usr = Users(name="yxh", email="152@11.com", extra="xxx")
# 通過 user對象 添加到session中
session.add(usr)
# 提交,才會刷新到數(shù)據(jù)庫中,不提交不會執(zhí)行
session.commit()
CRUD
創(chuàng)建(Create)、讀?。≧ead)、更新(Update)和刪除(Delete)
基礎(chǔ)操作
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.sql import text
from model import engine
from db_model import Users
# 定義一個 session
Session = sessionmaker(bind=engine)
# session = Session()
session = scoped_session(Session) # 后續(xù)使用這個session就是線程安全的
# 1 增加操作
obj1 = Users(name="yxh003")
session.add(obj1)
# 增加多個,不同對象
session.add_all([
Users(name="yxh009"),
Users(name="yxh008"),
])
session.commit()
# 2 刪除操作---》查出來再刪---》
session.query(Users).filter(Users.id > 2).delete()
session.commit()
# 3 修改操作--》查出來改 傳字典
session.query(Users).filter(Users.id > 0).update({"name": "yxh"})
# 類似于django的F查詢
# 字符串加
session.query(Users).filter(Users.id > 0).update({Users.name: Users.name + "099"}, synchronize_session=False)
# 數(shù)字加
session.query(Users).filter(Users.id > 0).update({"age": Users.age + 1}, synchronize_session="evaluate")
session.commit()
# 4 查詢操作----》
r1 = session.query(Users).all() # 查詢所有
# 只取age列,把name重命名為xx
# select name as xx,age from user;
r2 = session.query(Users.name.label('xx'), Users.email).all()
# filter傳的是表達(dá)式,filter_by傳的是參數(shù)
r3 = session.query(Users).filter(Users.name == "yxh").all()
r3 = session.query(Users).filter(Users.id >= 1).all()
r4 = session.query(Users).filter_by(name='yxh').all()
r5 = session.query(Users).filter_by(name='yxh').first()
# :value 和:name 相當(dāng)于占位符,用params傳參數(shù)
r6 = session.query(Users).filter(text("id<:value and name=:name")).params(value=224, name='yxh').order_by(
Users.id).all()
# 自定義查詢sql
r7 = session.query(Users).from_statement(text("SELECT * FROM users where name=:name")).params(name='yxh').all()
# 執(zhí)行原生sql
# 查詢
cursor = session.execute("select * from users")
result = cursor.fetchall()
# 添加
cursor = session.execute('insert into users(name) values(:value)', params={"value": 'yxh'})
session.commit()
print(cursor.lastrowid)
進(jìn)階操作
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.sql import text, func
from sqlalchemy import and_, or_
from model import engine
from db_model import Users, Person, Favor
# 定義一個 session
Session = sessionmaker(bind=engine)
# session = Session()
session = scoped_session(Session) # 后續(xù)使用這個session就是線程安全的
# 條件
# select * form user where name =lqz
ret = session.query(Users).filter_by(name='lqz').all()
# 表達(dá)式,and條件連接
# select * from user where id >1 and name = lqz
ret = session.query(Users).filter(Users.id > 1, Users.name == 'lqz').all()
# select * from user where id between 1,3 and name = lqz
ret = session.query(Users).filter(Users.id.between(1, 3), Users.name == 'lqz').all()
# # 注意下劃線
# select * from user where id in (1,3,4)
ret = session.query(Users).filter(Users.id.in_([1, 3, 4])).all()
# # ~非,除。。外
# select * from user where id not in (1,3,4)
ret = session.query(Users).filter(~Users.id.in_([1, 3, 4])).all()
# # 二次篩選
ret = session.query(Users).filter(Users.id.in_(session.query(Users.id).filter_by(name='lqz'))).all()
# # or_包裹的都是or條件,and_包裹的都是and條件
ret = session.query(Users).filter(and_(Users.id > 3, Users.name == 'eric')).all()
ret = session.query(Users).filter(or_(Users.id < 2, Users.name == 'eric')).all()
ret = session.query(Users).filter(
or_(
Users.id < 2,
and_(Users.name == 'eric', Users.id > 3),
Users.extra != ""
)).all()
# 通配符,以e開頭,不以e開頭
ret = session.query(Users).filter(Users.name.like('e%')).all()
ret = session.query(Users).filter(~Users.name.like('e%')).all()
# # 限制,用于分頁,區(qū)間
ret = session.query(Users)[1:2]
# # 排序,根據(jù)name降序排列(從大到?。?ret = session.query(Users).order_by(Users.id.desc()).all()
# # 第一個條件重復(fù)后,再按第二個條件升序排
ret = session.query(Users).order_by(Users.name.desc(), Users.id.asc()).all()
# 分組
# select * from user group by user.extra;
ret = session.query(Users).group_by(Users.extra).all()
# # 分組之后取最大id,id之和,最小id
# select max(id),sum(id),min(id) from user group by name ;
ret = session.query(
func.max(Users.id),
func.sum(Users.id),
func.min(Users.id)).group_by(Users.name).all()
# haviing篩選
# select max(id),sum(id),min(id) from user group by name having min(id)>2;
ret = session.query(
func.max(Users.id),
func.sum(Users.id),
func.min(Users.id)).group_by(Users.name).having(func.min(Users.id) > 2).all()
# select max(id),sum(id),min(id) from user where id >=1 group by name having min(id)>2;
ret = session.query(
func.max(Users.id),
func.sum(Users.id),
func.min(Users.id)).filter(Users.id >= 1).group_by(Users.name).having(func.min(Users.id) > 2).all()
# 連表(默認(rèn)用orm中forinkey關(guān)聯(lián))
# select * from user,favor where user.id=favor.id
ret = session.query(Users, Favor).filter(Users.id == Favor.nid).all()
# join表,默認(rèn)是inner join
# select * from Person inner join favor on person.favor=favor.id;
ret = session.query(Person).join(Favor).all()
# isouter=True 外連,表示Person left join Favor,沒有右連接,反過來即可
ret = session.query(Person).join(Favor, isouter=True).all()
ret = session.query(Favor).join(Person, isouter=True).all()
# 打印原生sql
aa = session.query(Person).join(Favor, isouter=True)
# print(aa)
# 自己指定on條件(連表條件),第二個參數(shù),支持on多個條件,用and_,同上
# select * from person left join favor on person.id=favor.id;
ret = session.query(Person).join(Favor, Person.id == Favor.id, isouter=True).all()
# 組合 UNION 操作符用于合并兩個或多個 SELECT 語句的結(jié)果集 多用于分表后 上下連表
# union和union all union 去重, union all 不去重
q1 = session.query(Users.name).filter(Users.id > 2)
q2 = session.query(Favor.caption).filter(Favor.nid < 2)
ret = q1.union(q2).all()
q1 = session.query(Users.name).filter(Users.id > 2)
q2 = session.query(Favor.caption).filter(Favor.nid < 2)
ret = q1.union_all(q2).all()
Flask集成sqlalchemy
'''
flask_migrate 中使用了flask_sqlalchemy 下載時,會自動幫你下載flask_sqlalchemy
flask_migrate 3.0之前和之后使用方法有區(qū)別。這里以2.x 做演示
'''
# flask_migrate使用步驟
from flask_sqlalchemy import SQLAlchemy
# Flask_SQLAlchemy給你包裝了基類,和session,以后拿到db
db = SQLAlchemy() # 全局SQLAlchemy,就是線程安全的,內(nèi)部就是上述那么實現(xiàn)的
app = Flask(__name__)
# SQLAlchemy 連接數(shù)據(jù)庫配置是在 config 配置字典中獲取的,所以需要我們將配置添加進(jìn)去
app.config.from_object('DevelopmentConfig')
'''
基本寫這些就夠了
"SQLALCHEMY_DATABASE_URI"
"SQLALCHEMY_POOL_SIZE"
"SQLALCHEMY_POOL_TIME"
"SQLALCHEMY_POOL_RECYCLE"
"SQLALCHEMY_TRACK_MODIFICATIONS"
"SQLALCHEMY_ENGINE_OPTIONS"
'''
# 將db注冊到app中,加載配置文件,flask-session,用一個類包裹一下app
db.init_app(app)
# 將需要使用orm操作的表通過繼承db.Model實現(xiàn)類
# 下面三句會創(chuàng)建出兩個命令:runserver db 命令(flask_migrate)
manager=Manager(app)
Migrate(app, db)
manager.add_command('db', MigrateCommand) # 添加一個db命令
使用命令:
1. python xxx.py db init # 初始化,剛開始干,生成一個migrate文件夾(遷移文件夾)
2. python xxx.py db migrate # 生成遷移版本,保存到遷移文件夾,同django makemigartions
3. python xxx.py db upgrade # 執(zhí)行遷移,同django migrate
配置選項 | 示例值 | 說明 |
---|---|---|
SQLALCHEMY_DATABASE_URI | 'sqlite:///database.db' |
數(shù)據(jù)庫連接字符串,指定要連接的數(shù)據(jù)庫類型、用戶名、密碼、主機(jī)和數(shù)據(jù)庫名稱等信息 |
SQLALCHEMY_TRACK_MODIFICATIONS | False |
是否跟蹤對象修改。默認(rèn)為True。建議在生產(chǎn)環(huán)境中將其設(shè)置為False,以提高性能 |
SQLALCHEMY_ECHO | True |
是否將生成的SQL語句輸出到控制臺上。主要用于調(diào)試和開發(fā),默認(rèn)為False |
SQLALCHEMY_POOL_SIZE | 10 |
數(shù)據(jù)庫連接池的大小,默認(rèn)為5 |
SQLALCHEMY_POOL_TIMEOUT | 20 |
等待數(shù)據(jù)庫連接的超時時間(秒),默認(rèn)為10 |
SQLALCHEMY_POOL_RECYCLE | 3600 |
連接在重新使用之前的最大時間(秒)。默認(rèn)為-1,表示禁用連接回收 |
SQLALCHEMY_NATIVE_UNICODE | False |
控制是否使用數(shù)據(jù)庫驅(qū)動程序的本機(jī)Unicode支持。默認(rèn)為False |
SQLALCHEMY_COMMIT_ON_TEARDOWN | True |
是否在請求結(jié)束時自動提交事務(wù)。默認(rèn)為False。在某些情況下,可以將其設(shè)置為True,以自動提交更改到數(shù)據(jù)庫 |
SQLALCHEMY_BINDS | {'users': 'sqlite:///users.db'} |
定義多個數(shù)據(jù)庫連接的綁定。 |
SQLALCHEMY_ENGINE_OPTIONS | {'pool_size':10, 'connect_timeout':20, 'encoding':'utf8mb4'} |
具體參數(shù)和效果取決于所使用的數(shù)據(jù)庫驅(qū)動程序和 SQLAlchemy 版本 |
構(gòu)建模型類
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# 相關(guān)配置
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/test31'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ECHO'] = True
# 創(chuàng)建組件對象
db = SQLAlchemy(app)
# 構(gòu)建模型類 類->表 類屬性->字段 實例對象->記錄
# 模型類必須繼承 db.Model, 其中 db 指對應(yīng)的組件對象
class User(db.Model):
__tablename__ = 't_user' # 設(shè)置表名, 表名默認(rèn)為類名小寫
id = db.Column(db.Integer, primary_key=True) # 設(shè)置主鍵, 默認(rèn)自增
name = db.Column('username', db.String(20), unique=True) # 設(shè)置字段名 和 唯一約束
age = db.Column(db.Integer, default=10, index=True) # 設(shè)置默認(rèn)值約束 和 索引
if __name__ == '__main__':
# 刪除所有繼承自db.Model的表
db.drop_all()
# 創(chuàng)建所有繼承自db.Model的表
db.create_all()
app.run(debug=True)
常用的字段類型
字段類型 | 示例 | 說明 |
---|---|---|
Integer | age = db.Column(db.Integer) |
整數(shù)類型 |
String | name = db.Column(db.String(100)) |
字符串類型 |
Text | content = db.Column(db.Text) |
長文本類型 |
Float | height = db.Column(db.Float) |
浮點數(shù)類型 |
Boolean | is_active = db.Column(db.Boolean, default=False) |
布爾類型 |
DateTime | created_at = db.Column(db.DateTime) |
日期和時間類型 |
Date | birth_date = db.Column(db.Date) |
日期類型 |
Time | meeting_time = db.Column(db.Time) |
時間類型 |
JSON | data = db.Column(db.JSON) |
存儲 JSON 數(shù)據(jù)的字段類型 |
PickleType | data = db.Column(db.PickleType) |
存儲 Python 對象的字段類型 |
Enum | status = db.Column(db.Enum('active', 'inactive', name='status_enum')) |
枚舉類型,用于限制字段取值范圍 |
ForeignKey | user_id = db.Column(db.Integer, db.ForeignKey('user.id')) |
外鍵類型,用于與其他模型類建立關(guān)聯(lián) |
relationship | user = db.relationship('User', backref='posts') |
定義模型之間的關(guān)系 |
backref | - | 在關(guān)系另一側(cè)創(chuàng)建反向引用 |
primary_key=True | id = db.Column(db.Integer, primary_key=True) |
將字段設(shè)置為主鍵 |
unique=True | email = db.Column(db.String(100), unique=True) |
將字段設(shè)置為唯一值 |
nullable=False | name = db.Column(db.String(100), nullable=False) |
設(shè)置字段為非空 |
default | is_active = db.Column(db.Boolean, default=False) |
設(shè)置字段的默認(rèn)值 |
index=True | username = db.Column(db.String(100), index=True) |
為字段創(chuàng)建索引 |
server_default | created_at = db.Column(db.DateTime, server_default=db.func.now()) |
在數(shù)據(jù)庫服務(wù)器上設(shè)置字段的默認(rèn)值 |
常用的字段選項
字段選項 | 示例 | 說明 |
---|---|---|
primary_key=True | id = db.Column(db.Integer, primary_key=True) |
將字段設(shè)置為主鍵 |
unique=True | email = db.Column(db.String(100), unique=True) |
將字段設(shè)置為唯一值 |
nullable=False | name = db.Column(db.String(100), nullable=False) |
設(shè)置字段為非空 |
default | is_active = db.Column(db.Boolean, default=False) |
設(shè)置字段的默認(rèn)值 |
index=True | username = db.Column(db.String(100), index=True) |
為字段創(chuàng)建索引 |
autoincrement=True | id = db.Column(db.Integer, primary_key=True, autoincrement=True) |
設(shè)置字段自增 |
server_default | created_at = db.Column(db.DateTime, server_default=db.func.now()) |
在數(shù)據(jù)庫服務(wù)器上設(shè)置字段的默認(rèn)值 |
onupdate | updated_at = db.Column(db.DateTime, onupdate=db.func.now()) |
在字段更新時設(shè)置新的值 |
foreign_key | user_id = db.Column(db.Integer, db.ForeignKey('user.id')) |
定義字段與其他表的外鍵關(guān)系 |
backref | posts = db.relationship('Post', backref='user') |
在關(guān)系的另一側(cè)創(chuàng)建反向引用 |
lazy | posts = db.relationship('Post', lazy='dynamic') |
控制字段加載的時機(jī) |
uselist | users = db.relationship('User', backref='role', uselist=False) |
控制關(guān)系字段是否作為列表返回 |
cascade | posts = db.relationship('Post', cascade='delete') |
控制級聯(lián)操作的行為 |
passive_deletes | posts = db.relationship('Post', passive_deletes=True) |
控制刪除時的級聯(lián)行為 |
passive_updates | posts = db.relationship('Post', passive_updates=True) |
控制更新時的級聯(lián)行為 |
ondelete | post_id = db.Column(db.Integer, db.ForeignKey('post.id', ondelete='CASCADE')) |
在刪除關(guān)聯(lián)記錄時的處理方式 |
primaryjoin | posts = db.relationship('Post', primaryjoin='and_(User.id == Post.user_id, User.is_active == True)') |
指定關(guān)系字段之間的條件表達(dá)式 |
secondary | users = db.relationship('User', secondary='user_role', backref='roles') |
定義多對多關(guān)系中的中間表 |
secondaryjoin | users = db.relationship('User', secondary='user_role', secondaryjoin='and_(User.id == user_role.user_id, user_role.is_active == True)') |
指定多對多關(guān)系中中間表與主表之間的條件表達(dá)式 |
增
# 1.創(chuàng)建模型對象
user1 = User(name='zs', age=20)
# user1.name = 'zs'
# user1.age = 20
# 2.將模型對象添加到會話中
db.session.add(user1)
# 添加多條記錄
# db.session.add_all([user1, user2, user3])
# 3.提交會話 (會提交事務(wù))
# sqlalchemy會自動創(chuàng)建隱式事務(wù)
# 事務(wù)失敗會自動回滾
db.session.commit()
查
# 查詢所有用戶數(shù)據(jù)
User.query.all() 返回列表, 元素為模型對象
# 查詢有多少個用戶
User.query.count()
# 查詢第1個用戶
User.query.first() 返回模型對象/None
# 查詢id為4的用戶[3種方式]
# 方式1: 根據(jù)id查詢 返回模型對象/None
User.query.get(4)
# 方式2: 等值過濾器 關(guān)鍵字實參設(shè)置字段值 返回BaseQuery對象
# BaseQuery對象可以續(xù)接其他過濾器/執(zhí)行器 如 all/count/first等
User.query.filter_by(id=4).all()
# 方式3: 復(fù)雜過濾器 參數(shù)為比較運算/函數(shù)引用等 返回BaseQuery對象
User.query.filter(User.id == 4).first()
# 查詢名字結(jié)尾字符為g的所有用戶[開始 / 包含]
User.query.filter(User.name.endswith("g")).all()
User.query.filter(User.name.startswith("w")).all()
User.query.filter(User.name.contains("n")).all()
User.query.filter(User.name.like("w%n%g")).all() # 模糊查詢
# 查詢名字和郵箱都以li開頭的所有用戶[2種方式]
User.query.filter(User.name.startswith('li'), User.email.startswith('li')).all()
from sqlalchemy import and_
User.query.filter(and_(User.name.startswith('li'), User.email.startswith('li'))).all()
# 查詢age是25 或者 `email`以`itheima.com`結(jié)尾的所有用戶
from sqlalchemy import or_
User.query.filter(or_(User.age==25, User.email.endswith("itheima.com"))).all()
# 查詢名字不等于wang的所有用戶[2種方式]
from sqlalchemy import not_
User.query.filter(not_(User.name == 'wang')).all()
User.query.filter(User.name != 'wang').all()
# 查詢id為[1, 3, 5, 7, 9]的用戶
User.query.filter(User.id.in_([1, 3, 5, 7, 9])).all()
# 所有用戶先按年齡從小到大, 再按id從大到小排序, 取前5個
User.query.order_by(User.age, User.id.desc()).limit(5).all()
# 查詢年齡從小到大第2-5位的數(shù)據(jù) 2 3 4 5
User.query.order_by(User.age).offset(1).limit(4).all()
# 分頁查詢, 每頁3個, 查詢第2頁的數(shù)據(jù) paginate(頁碼, 每頁條數(shù))
pn = User.query.paginate(2, 3)
pn.pages 總頁數(shù) pn.page 當(dāng)前頁碼 pn.items 當(dāng)前頁的數(shù)據(jù) pn.total 總條數(shù)
# 查詢每個年齡的人數(shù) select age, count(name) from t_user group by age 分組聚合
from sqlalchemy import func
data = db.session.query(User.age, func.count(User.id).label("count")).group_by(User.age).all()
for item in data:
# print(item[0], item[1])
print(item.age, item.count) # 建議通過label()方法給字段起別名, 以屬性方式獲取數(shù)據(jù)
# 只查詢所有人的姓名和郵箱 優(yōu)化查詢 User.query.all() # 相當(dāng)于select *
from sqlalchemy.orm import load_only
data = User.query.options(load_only(User.name, User.email)).all() # flask-sqlalchem的語法
for item in data:
print(item.name, item.email)
data = db.session.query(User.name, User.email).all() # sqlalchemy本體的語法
for item in data:
print(item.name, item.email)
改
先查詢, 再更新
對應(yīng)SQL中的 先select, 再commit()
-
缺點
- 查詢和更新分兩條語句, 效率低
- 如果并發(fā)更新, 可能出現(xiàn)更新丟失問題(Lost Update)
-
示例文章來源地址http://www.zghlxwxcb.cn/news/detail-548878.html
# 1.執(zhí)行查詢語句, 獲取目標(biāo)模型對象 goods = Goods.query.filter(Goods.name == '方便面').first() # 2.對模型對象的屬性進(jìn)行賦值 (更新數(shù)據(jù)) goods.count = goods.count - 1 # 3.提交會話 db.session.commit()
基于過濾條件的更新 (推薦方案)
對應(yīng)SQL中的 update xx where xx = xx (也稱為 update子查詢 )
-
優(yōu)點
- 一條語句, 被網(wǎng)絡(luò)IO影響程度低, 執(zhí)行效率更高
- 查詢和更新在一條語句中完成, 單條SQL具有原子性, 不會出現(xiàn)更新丟失問題
- 會對滿足過濾條件的所有記錄進(jìn)行更新, 可以實現(xiàn)批量更新處理
-
示例
Goods.query.filter(Goods.name == '方便面').update({'count': Goods.count - 1}) # 提交會話 db.session.commit()
刪
先查詢, 再刪除
# 方式1: 先查后刪除
goods = Goods.query.filter(Goods.name == '方便面').first()
# 刪除數(shù)據(jù)
db.session.delete(goods)
# 提交會話 增刪改都要提交會話
db.session.commit()
基于過濾條件的刪除 (推薦方案)
# 方式2: delete子查詢
Goods.query.filter(Goods.name == '方便面').delete()
# 提交會話
db.session.commit()
到了這里,關(guān)于【Flask】SQLAlchemy的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!