5 數(shù)據(jù)庫
這一章學(xué)習(xí)如何在Python中使用DBMS(數(shù)據(jù)庫管理系統(tǒng)),來對數(shù)據(jù)庫進(jìn)行管理和操作。本書使用SQLite作為示例。
注:按下Ctrl+F5,或Shift+F5可以清除瀏覽器緩存。
5.1 數(shù)據(jù)庫的分類
分為SQL(Structured Query Language)數(shù)據(jù)庫和NoSQL(Not Only SQL)數(shù)據(jù)庫。
- SQL:稍顯復(fù)雜,但不容易出錯,可以適應(yīng)大部分場景。
-
NoSQL:靈活,效率高,可擴(kuò)展性好等。
- 1、文檔存儲:使用類json格式來表示數(shù)據(jù)
- 2、鍵值對存儲:通過鍵來存取數(shù)據(jù),讀寫很快,常作為緩存使用。
5.2 ORM
ORM:Object-Relational Mapping,對象關(guān)系映射。
作用:
- 處理查詢參數(shù)的轉(zhuǎn)義,防止注入。
- 為不同的DBMS提供統(tǒng)一的接口。
- 能直接使用Python操作數(shù)據(jù)庫,不需要寫SQL語句。
ORM實現(xiàn)了三層映射關(guān)系:表 --> Python類,字段 --> 類屬性,記錄 --> 類實例。
# 定義表
from foo_orm import Model, Column, String
class Contact(Model):
__tablename__ = 'contacts'
name = Column(String(100), nullable=False)
# 插入記錄
contact = Contact(name="zhang san")
5.3 使用Flask_SQLAlchemy
1、連接數(shù)據(jù)庫:
首先連接數(shù)據(jù)庫需要指定URI(Uniform Resource Identifier,統(tǒng)一資源標(biāo)識符),URL(統(tǒng)一資源定位符)是它的子集。
常用的數(shù)據(jù)庫URI格式:(p143)
SQLite是基于文件的DBMS,不需要數(shù)據(jù)庫服務(wù)器,只需要指定數(shù)據(jù)庫文件的絕對路徑。配置數(shù)據(jù)庫URI的代碼如下:
from flask import Flask
import os
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
t = app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URL', 'sqlite:///' + os.path.join(app.root_path, 'data.db'))
db = SQLAlchemy(app)
print(t)
print(db)
運行輸出:
sqlite:///D:\code_all\gitCode\helloflask_learn\數(shù)據(jù)庫\data.db
<SQLAlchemy>
補充:
os.getenv
是一個Python標(biāo)準(zhǔn)庫函數(shù),它用于從環(huán)境變量中獲取指定的值。這個函數(shù)的第一個參數(shù)是要查找的環(huán)境變量名稱,第二個參數(shù)是默認(rèn)值,如果未找到指定的環(huán)境變量,則返回這個默認(rèn)值。
2、定義數(shù)據(jù)庫模型:
模型類繼承自SQLAlchemy提供的db.Model基類,表的字段由db.Column類的實例表示。
SQLAlchemy常用的字段類型:(p144)
常用的字段參數(shù):(p145)
class Note(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text)
-
表名稱會根據(jù)模型的類名稱自動生成,可用
__tablename__
屬性指定。 -
字段名默認(rèn)為類屬性名,看用
name
關(guān)鍵字參數(shù)指定。
3、創(chuàng)建數(shù)據(jù)庫和表:
db.create_all()
可以查看模型對象的建表SQL語句:
from app import Note
from sqlalchemy.schema import CreateTable
print(CreateTable(Note.__table__))
改動模型類后,再次調(diào)用create_all()
不會更新表結(jié)構(gòu)??梢哉{(diào)用drop_all()
方法刪除數(shù)據(jù)庫和表,然后重建。
可以自定義一個flaks命令完成數(shù)據(jù)庫的創(chuàng)建工作,對于sqlite創(chuàng)建成功后會生成一個數(shù)據(jù)庫文件,如data.db
。
import click
@app.cli.command()
def initdb():
db.create_all()
click.echo('Initialized database')
5.4 數(shù)據(jù)庫操作
SQLAlchemy使用數(shù)據(jù)庫會話(也稱為事務(wù))來管理數(shù)據(jù)庫操作,會話代表一個臨時存儲區(qū),對會話對象調(diào)用commit()
方法時,改動才被提交到數(shù)據(jù)庫。調(diào)用rollback()
方法可以撤銷會話中未提交的改動。
1、CRUD:
即Create、Read、Update、Delete。
- Create
note = Note(body='hello, world')
db.session.add(note)
db.session.commit()
通過add_all()
可以一次提交一個列表。
- Read
<模型類>.query.<過濾方法>.<查詢方法>
Note.query.filter(Note.body=='hello, world').first
Query對象調(diào)用過濾方法的返回值仍然是一個Query對象,就像SQL的操作對象和返回結(jié)果都是表。查詢方法返回的是模型類實例。
常用的SQLAchemy查詢方法:(p148)
all()
,first()
,count()
,paginate()
,get(ident)
等常用過濾方法:(p150)
filter()
,filter_by()
,order_by()
,limit()
,group_by()
等常用查詢操作符:(p150) LIKE,IN,NOT IN,AND,OR等。
- Update
note = Note.query.get(2)
note.body = 'hello, flask'
db.session.commit()
- Delete
note = Note.query.get(2)
db.session.delete(note)
db.session.commit()
2、在視圖函數(shù)里操作數(shù)據(jù)庫:
與在python shell中基本一致。
5.5 定義關(guān)系
1、配置Python Shell上下文
使用app.shell_context_processor
裝飾器注冊一個shell上下文處理函數(shù),返回包含變量和變量值的字典。
@app.shell_context_processor
def make_shell_context():
return dict(db=db, Note=Note)
2、一對多關(guān)系
定義一對多關(guān)系包含兩個部分:定義外鍵,和定義關(guān)系屬性。其中關(guān)系屬性相當(dāng)于一個快捷查詢,不會作為字段被寫入到數(shù)據(jù)庫中。下面的關(guān)系屬性articles
會返回該作者所有文章的記錄列表。
class Author(db.Model):
...
articles = db.relationship('Article')
class Article(db.Model):
...
author_id = db.Column(db.Integer, db.ForeignKey('athor.id'))
可以在兩側(cè)都定義一個關(guān)系屬性,稱為雙向關(guān)系,需要用到back_populates
關(guān)鍵字參數(shù),值為另一側(cè)的關(guān)系屬性名。
class Author(db.Model):
...
articles = db.relationship('Article', back_populates='author')
class Article(db.Model):
...
author_id = db.Column(db.Integer, db.ForeignKey('athor.id'))
author = db.relationship('Author', back_populates='articles')
可以使用
backref
簡化關(guān)系定義,(p163)疑惑:為什么要手動在兩側(cè)都指定反向引用,而不是添加了外鍵屬性之后就自動的呢?是采用了數(shù)據(jù)庫中的索引嗎?
定義關(guān)系后,建立關(guān)系有兩種方式,一種是為外鍵字段賦值,另一種是通過操作關(guān)系屬性。
# 為外鍵字段賦值
article_A.author_id = 1
# 操作關(guān)系屬性: append, remove, pop
Mike.articles.append(article_A)
常用關(guān)系函數(shù)參數(shù):p161
常用關(guān)系記錄加載方式:p161
3、一對一關(guān)系
實際上是在通過建立一對多關(guān)系的雙向關(guān)系的基礎(chǔ)上轉(zhuǎn)化而來,只是在原來”一“的一方設(shè)置userlist=False
,將集合屬性變?yōu)闃?biāo)量屬性。此后,無法再使用列表語義操作,如append
方法。
class Contry(db.Model):
...
capital = db.relationship('Capital', uselist=False)
class Capital(db.Model):
...
contry_id = db.Column(db.Integer, db.ForeignKey('country.id'))
contry = db.relationship('Country')
4、多對多關(guān)系
一對多關(guān)系中在“多”的一方存放外鍵,則“多”一方的每條記錄只能有一條關(guān)系。我們可以單獨創(chuàng)建一個關(guān)聯(lián)表(db.Table
)來存儲外鍵,表示多對多關(guān)系。使用secondary
參數(shù)來指定關(guān)聯(lián)表。
association_table = db.Table(
'association',
db.Column('student_id', db.Integer, db.ForeignKey('student.id')),
db.Column('teacher_id', db.Integer, db.ForeignKey('teacher.id')))
class Student(db.Model):
...
teachers = db.relationship('Teacher', secondary=association_table, back_populates='students')
class Teacher(db.Model):
...
students = db.relationship('Student', secondary=association_table, back_populates='teachers')
5.6 更新數(shù)據(jù)庫表
1、重新生成表
方法很簡單,但缺點是會丟失原來的所有數(shù)據(jù)。
db.drop_all()
db.create_all()
2、使用Flask-Migrate遷移(p169)
可以保留數(shù)據(jù)庫中原有的數(shù)據(jù)。自動生成的遷移命令不一定可靠,必要時檢查一下。
flask db init # 創(chuàng)建遷移環(huán)境
flask db migrate # 生成遷移腳本
flask db upgrade # 應(yīng)用遷移
flask db downgrade # 撤銷一次遷移
5.7 數(shù)據(jù)庫進(jìn)階
1、級聯(lián)操作(p172)
在relationship
方法可以配置cascade
參數(shù),所有可用值為save-update,merge,refresh-expire,expunge,delete。默認(rèn)值為save-update,merge
。
class Post(db.Model):
...
comments = db.relationship(..., cascade='save-update, merge, delete')
-
save-update:
db.session.add()
將Post對象添加到數(shù)據(jù)庫會話時,相關(guān)的Comment對象也會被添加到數(shù)據(jù)庫會話。 -
delete:Post記錄被刪除時,相關(guān)的Comment記錄也會被刪除。
-
delete-orphan:Post與Comment記錄解除關(guān)系操作時,相應(yīng)的Comment記錄會被刪除。
-
all:包含除了
delete-orphan
之外的所有可用值。
2、事件監(jiān)聽(p176)
在Flask中有請求回調(diào)函數(shù),而SQLAlchemy也提供了listens_for()
裝飾器來注冊事件回調(diào)函數(shù)。裝飾器接受兩個參數(shù),target
表示監(jiān)聽的對象,identifier
表示被監(jiān)聽事件的類型。被注冊的監(jiān)聽函數(shù)需要接收對應(yīng)事件方法的所有參數(shù)。
疑惑:“事件方法”指什么?怎么知道它有哪些參數(shù)?
class Draft(db.Model):
...
edit_time = ...
@db.event.listens_for(Draft.body, 'set', named=True)
def increment_edit_time(**kwargs):
if kwargs['target'].edit_time is not None:
kwargs['target'].edit_time += 1
設(shè)置named
參數(shù)為True可以使用kwargs
接收所有參數(shù)(不知道為啥)。kwargs
中的target
參數(shù)表示觸發(fā)事件的模型類實例。
小結(jié)
SQLAlchemy入門教程:(p177)http://docs.sqlalchemy.org/en/latest/orm/tutorial.html文章來源:http://www.zghlxwxcb.cn/news/detail-708657.html
數(shù)據(jù)庫這一章我看得有點拖拉,正值開學(xué),可能需要好些天才能找回學(xué)習(xí)狀態(tài)。大部分東西我還是之前都有所了解,因此看得比較流暢。在最近開發(fā)自己的玩具程序的過程中,數(shù)據(jù)庫這一環(huán)節(jié)可給我制造了不少麻煩(特別是配環(huán)境),它在我眼里的黑盒程度又比較高,書中說到本章只是一個簡單的介紹,不過暫時于我也夠用了。文章來源地址http://www.zghlxwxcb.cn/news/detail-708657.html
到了這里,關(guān)于Flask狼書筆記 | 05_數(shù)據(jù)庫的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!