第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Flask 的 ORM 模型 - 概述

在詞條使用 Python 操作 MySQL 數(shù)據(jù)庫(kù)中,通過(guò) SQL 語(yǔ)句訪問(wèn)數(shù)據(jù)庫(kù),繁瑣易錯(cuò)。本小節(jié)介紹了用于簡(jiǎn)化訪問(wèn)數(shù)據(jù)庫(kù)的 ORM 模型,ORM 模型定義了關(guān)系數(shù)據(jù)庫(kù)和對(duì)象的映射關(guān)系,使得訪問(wèn)數(shù)據(jù)庫(kù)的代碼簡(jiǎn)單清晰、易于維護(hù)。

1. 問(wèn)題的產(chǎn)生

訪問(wèn)關(guān)系數(shù)據(jù)庫(kù)的傳統(tǒng)方式是:拼接 SQL 語(yǔ)句。例如,向數(shù)據(jù)庫(kù)中插入一條數(shù)據(jù),根據(jù)要插入的數(shù)據(jù)拼接一條 SQL INSERT 語(yǔ)句,下面的 Python 程序使用 SQL 語(yǔ)句向數(shù)據(jù)庫(kù)中插入一條學(xué)生的信息:

sno = '20201916'
name = '張三'
age = 20
gender = 'male'
sql = 'INSERT INTO students(sno, name, age, gender) VALUES("%s", "%s", %d, "%s")' % (sno, name, age, gender)
rows = cursor.execute(sql)

在第 5 行,Python 程序使用字符串運(yùn)算符 % 根據(jù)參數(shù) sno、name、age 和 gender 最終生成一條 SQL 語(yǔ)句:

INSERT INTO students(sno, name, age, gender) VALUES("", "張三", 20, "male");

隨著項(xiàng)目越來(lái)越大,通過(guò)拼接 SQL 語(yǔ)句訪問(wèn)數(shù)據(jù)庫(kù)存在如下的問(wèn)題:

1. 繁瑣易錯(cuò)

在上面的例子中,第 5 行代碼用于拼接 INSERT 語(yǔ)句,INSERT 語(yǔ)句需要插入 4 個(gè)字段,該行代碼較長(zhǎng),無(wú)法在一行顯示。在實(shí)際的軟件開發(fā)中,INSERT 語(yǔ)句可能需要插入 10 個(gè)以上的字段,那么拼接 INSERT 語(yǔ)句的代碼則非常的繁瑣易錯(cuò)。

下面的 SQL 語(yǔ)句來(lái)自于一個(gè)實(shí)際的項(xiàng)目:

sql = "INSERT INTO Flights(FlightID, AircraftModel, RegisterID, Direction, ExpectApronTime, RunwayID, ApronID, AirwayID, TaxiwayTimes, AirwayTimes, Rank) VALUES('%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', %d)" % (flightID, aircraftModel, registerID, direction, expectApronTime, runwayID, apronID, airwayID, taxiwayTimes, airwayTimes, rank)

要插入的數(shù)據(jù)包含有 11 個(gè)字段,造成 SQL 語(yǔ)句非常的冗長(zhǎng),需要在多行中才能完全顯示,程序的可讀性極差。

2. SQL 語(yǔ)句重復(fù)利用率低

越復(fù)雜的 SQL 語(yǔ)句條件越多、代碼越長(zhǎng),在實(shí)際的項(xiàng)目中,會(huì)出現(xiàn)很多很相近的 SQL 語(yǔ)句。

3. Web 安全漏洞

直接使用 SQL 語(yǔ)句存在有 Web 安全漏洞的問(wèn)題:通過(guò)把 SQL 命令插入到頁(yè)面請(qǐng)求的查詢字符串,最終達(dá)到欺騙服務(wù)器執(zhí)行惡意的 SQL 命令。

下面的 SQL 語(yǔ)句根據(jù)頁(yè)面請(qǐng)求中的用戶名和密碼查詢數(shù)據(jù)庫(kù):

username = 從頁(yè)面請(qǐng)求中獲取用戶名
password = 從頁(yè)面請(qǐng)求中獲取密碼
sql = 'select * from users where username = "%s" and password = "%s"' % (username, password)

在第 3 行的 SELECT 語(yǔ)句中,where 條件進(jìn)行權(quán)限檢查,只有 username 和 password 與數(shù)據(jù)庫(kù)表 users 中的數(shù)據(jù)匹配時(shí),才返回有效數(shù)據(jù),因此,只有用戶輸入正確的用戶名和密碼才可以獲取數(shù)據(jù)。

這條 SQL 語(yǔ)句存在有安全漏洞,假設(shè)用戶在頁(yè)面中輸入的用戶名為 admin"# (共 7 個(gè)字符,前 5 個(gè)字符是 admin,后面 2 個(gè)字符是 " 和 #),密碼為 123456,則最終拼接的 SQL 語(yǔ)句如下:

select * from users where username = "admin"#" and password = "123456"

在 SQL 中,# 是行注釋,因此上述 SQL 語(yǔ)句相當(dāng)于:

select * from users where username = "admin"

只要數(shù)據(jù)庫(kù)表 users 中有 admin 這條記錄,執(zhí)行該條 SQL 語(yǔ)句就會(huì)返回?cái)?shù)據(jù),這樣對(duì) password 的檢查就徹底失效了。

2. 對(duì)象 - 關(guān)系映射 (ORM)

隨著面向?qū)ο蟮能浖_發(fā)方法發(fā)展,出現(xiàn)了對(duì)象 - 關(guān)系映射 (Object Relation Mapping) 模型,簡(jiǎn)稱為 ORM,ORM 通過(guò)使用描述對(duì)象和數(shù)據(jù)庫(kù)之間映射的元數(shù)據(jù),將面向?qū)ο笳Z(yǔ)言程序中的對(duì)象自動(dòng)持久化到關(guān)系數(shù)據(jù)庫(kù)中。

圖片描述

ORM 描述的對(duì)象關(guān)系映射如上圖圖所示:

  • 關(guān)系數(shù)據(jù)庫(kù)中的表對(duì)應(yīng)于面向?qū)ο笾械念悾?/li>
  • 關(guān)系數(shù)據(jù)庫(kù)中的數(shù)據(jù)行(記錄)對(duì)應(yīng)于面向?qū)ο笾械膶?duì)象;
  • 關(guān)系數(shù)據(jù)庫(kù)中的字段對(duì)應(yīng)于面向?qū)ο笾械膶傩浴?/li>

假設(shè)關(guān)系數(shù)據(jù)庫(kù)中存在一張表 Students,包括 sno、name 和 age 等字段,使用如下 SQL 語(yǔ)句進(jìn)行創(chuàng)建:

CREATE TABLE students(
    sno VARCHAR(255),
    name VARCHAR(255),
    age INT
);

在 ORM 模型中,存在一個(gè)類 Student 與關(guān)系數(shù)據(jù)庫(kù)中的表 students 相對(duì)應(yīng),代碼如下所示:

class Student:
    def __init__(self, sno, name, age):
        self.sno = sno
        self.name = name
        self.age = age

tom = Student('1918001', 'tom', 12)        

在第 7 行,程序通過(guò)類 Student 實(shí)例化生成一個(gè)對(duì)象 student。在這個(gè)具體的例子中,對(duì)象和數(shù)據(jù)庫(kù)之間映射如下表所示:

關(guān)系數(shù)據(jù)庫(kù)中的概念 面向?qū)ο笾械母拍?/th>
表 students 類 Student
表 students 中的一條記錄 對(duì)象 tom
字段 sno、name 和 age 屬性 sno、name 和 age

3. SQLAlchemy 簡(jiǎn)介

SQLAlchemy 是 Python 中一個(gè)通過(guò) ORM 操作數(shù)據(jù)庫(kù)的框架。SQLAlchemy 對(duì)象關(guān)系映射器提供了一種方法,用于將用戶定義的 Python 類與數(shù)據(jù)庫(kù)表相關(guān)聯(lián),并將這些類實(shí)例與其對(duì)應(yīng)表中的行相關(guān)聯(lián)。SQLAlchemy 可以讓開發(fā)者使用類和對(duì)象的方式操作數(shù)據(jù)庫(kù),從而從繁瑣的 sql 語(yǔ)句中解脫出來(lái)。

SQLAlchemy 的架構(gòu)如下所示:

圖片描述

圖片來(lái)源于網(wǎng)絡(luò)

在 SQLAlchemy 的核心架構(gòu)中,Schema / Types 定義了類到表之間的映射規(guī)則。DBAPI 是訪問(wèn)關(guān)系數(shù)據(jù)庫(kù)的底層接口,底層接口仍然通過(guò) SQL 語(yǔ)句訪問(wèn)關(guān)系數(shù)據(jù)庫(kù)。SQLAlchemy 支持多種關(guān)系數(shù)據(jù)庫(kù) (Oracle, Postgresql, Mysql),Dialect 根據(jù)用戶的配置,調(diào)用不同的數(shù)據(jù)庫(kù)底層訪問(wèn) API,并執(zhí)行對(duì)應(yīng)的 SQL 語(yǔ)句。

4. 使用 SQLAlchemy 完成映射

本小節(jié)講解在 Flask 中使用 SQLAlchemy 完成表與對(duì)象的映射,分為如下步驟:

4.1 安裝相關(guān)庫(kù)

$ pip3 install flask
$ pip3 install pymysql
$ pip3 install SQLAlchemy
$ pip3 install flask-sqlalchemy

4.2 創(chuàng)建數(shù)據(jù)庫(kù)

在 mysql 數(shù)據(jù)庫(kù)中執(zhí)行如下 SQL 腳本 db.sql:

DROP DATABASE IF EXISTS school;
CREATE DATABASE school;
USE school;

CREATE TABLE students(
    sno INT,
    name VARCHAR(255),
    age INT,
    PRIMARY KEY(sno)
);

INSERT students(sno, name, age) VALUES(1, 'tom', 11);
INSERT students(sno, name, age) VALUES(2, 'jerry', 12);
INSERT students(sno, name, age) VALUES(3, 'mike', 13);

首先,如果存在數(shù)據(jù)庫(kù) school 則刪除,然后建立一個(gè)新的、空的數(shù)據(jù)庫(kù) school;然后,創(chuàng)建表 students;最后,向數(shù)據(jù)庫(kù)的表 students 中插入 3 條記錄用于測(cè)試。

4.3 創(chuàng)建 SQLAlchemy 對(duì)象

創(chuàng)建文件 db.py,創(chuàng)建 SQLAlchemy 對(duì)象,如下所示:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

user = 'root'
password = '123456'
database = 'school'
uri = 'mysql+pymysql://%s:%s@localhost:3306/%s' % (user, password, database)
app.config['SQLALCHEMY_DATABASE_URI'] = uri
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)

首先引入庫(kù) flask 和庫(kù) flask_sqlalchemy;然后對(duì) SQLAlchemy 進(jìn)行配置,設(shè)置如下參數(shù):

參數(shù)
user 訪問(wèn)數(shù)據(jù)庫(kù)的用戶,假設(shè)是 root
password 訪問(wèn)數(shù)據(jù)庫(kù)的密碼,假設(shè)是 123456
database 數(shù)據(jù)庫(kù)名稱
uri SQLAlchemy 連接數(shù)據(jù)庫(kù)的字符串

在第 10 行,對(duì) SQLAlchemy 進(jìn)行配置,SQLALCHEMY_DATABASE_URI 配置的是連接數(shù)據(jù)庫(kù)的字符串,在這個(gè)例子中,該字符串為:

mysql+pymysql://root:123456@localhost:3306/school

該字符串包含有數(shù)據(jù)庫(kù)類型、用戶名、密碼、數(shù)據(jù)庫(kù)名等信息,含義如下:

字符串 含義
mysql+pymysql 數(shù)據(jù)庫(kù)類型是 mysql,使用 pymysql 作為訪問(wèn) mysql 的底層 API
root 訪問(wèn)數(shù)據(jù)庫(kù)的用戶
123456 訪問(wèn)數(shù)據(jù)庫(kù)的密碼
school 數(shù)據(jù)庫(kù)名稱

最后,在第 13 行,創(chuàng)建 SQLAlchemy 對(duì)象 db。

4.3 建立類與表之間的映射

最核心的工作是建立類與表之間的映射,代碼如下:

class Student(db.Model):
    __tablename__ = 'students'
    sno = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    age = db.Column(db.Integer)

建立表和類的映射關(guān)系:在第 1 行,創(chuàng)建類 Student 繼承于 db.Model,表示類 Student 用于映射數(shù)據(jù)庫(kù)中的表;在第 2 行,設(shè)定 __tablename__ 為 students,表示將類 Student 映射到數(shù)據(jù)庫(kù)中的表 students。

建立屬性和字段的映射關(guān)系:在第 3 行,映射 sno 到表 students 的字段 sno,類型為整數(shù) (db.Integer),primary_key=True 表示該字段是主鍵;在第 4 行,映射 name 到表 students 的字段 name,類型為整數(shù) (db.String); 在第 5 行,映射 age 到表 students 的字段 age,類型為整數(shù) (db.Integer)。

4.4 使用面向?qū)ο蟮恼Z(yǔ)法訪問(wèn)數(shù)據(jù)庫(kù)

使用 ORM 模型定義了關(guān)系數(shù)據(jù)庫(kù)和對(duì)象的映射關(guān)系后,可以使用面向?qū)ο蟮恼Z(yǔ)法訪問(wèn)數(shù)據(jù)庫(kù),如下所示:

students = Student.query.all()
for student in students:
    print(student.sno, student.name, student.age)

在第 1 行,類 Student.query.all () 返回所有的學(xué)生,相當(dāng)于使用 SQL 語(yǔ)句 “SELECT * from students” 查詢所有的學(xué)生;在第 3 行,通過(guò) student.sno、student.name、student.age 即可訪問(wèn)數(shù)據(jù)庫(kù)中一條記錄的相關(guān)字段。

程序運(yùn)行輸出如下:

1 tom 11
2 jerry 12
3 mike 13

在 4.2 小節(jié)中,使用 INSERT 語(yǔ)句插入了 3 條測(cè)試數(shù)據(jù),因此輸出顯示了這 3 條數(shù)據(jù)。

4. 源代碼下載

5. 小結(jié)

本節(jié)介紹 ORM 模型的相關(guān)概念,使用思維導(dǎo)圖概括如下:

圖片描述

本節(jié)通過(guò)一個(gè)實(shí)例講解了如何在 Flask 中建立面向?qū)ο笈c關(guān)系數(shù)據(jù)庫(kù)映射關(guān)系,在下一個(gè)小節(jié)中通過(guò)一個(gè)更完整的實(shí)例講解如何使用 ORM 進(jìn)行增刪改查。