05-01 數據庫


05-數據庫

1554281623399

數據庫,顧名思義是儲存數據的倉庫,常見的管理數據庫的軟件被稱為數據庫管理系統(DBMS, Database Management System), 常見的DBMS有 MySQL、PostgreSQL、SQLite、MongoDB。這些常見的DBMS我們可以把他們理解為專門負責搬運數據的管理的數據的程序。

1 什么是ORM?

對象關系映射(英語:(Object Relational Mapping,簡稱ORM,或O/RM,或O/R mapping),是一種程序技術,用於實現面向對象編程語言里不同類型系統的數據之間的轉換 。 ORM是“對象-關系-映射”的簡稱。在我們的web應用開發中ORM把底層的SQL數據實體轉化成高層的Python對象。只需要通過Python代碼即可完成數據庫操作。

2 為什么要有ORM?

在web應用里使用原生的SQL語句操作數據庫固然能達到我們處理儲存數據的需求,但是會存在以下三類問題:

  • 手動編寫SQL語句比較復雜耗時(當然因人而異,如果熱衷於原生sql,並不影響開發),並且視圖函數中寫大量SQL語句會降低代碼的易讀性。
  • 比較容易出現安全問題,如SQL注入。
  • 對於不同的DBMS,需要使用不同的Python接口庫,語法各不相同,很難有標准化的代碼流程。

使用ORM可以很大程度上解決這些問題,在python中,ORM把底層的SQL數據實體轉化成高層的Python對象。這樣的好處是,你甚至不需要了解SQL,只需要操作Python對象的即可完成數據庫操作。

使用ORM的優勢:

  • 提升開發效率。從高層對象轉換成原生SQL會犧牲一些性能,但這微不足道的性能犧牲換取的是巨大開發效率提升。
  • 可移植性好。它實現了數據庫模型與DEMS的解耦,即數據模型的設計不需要依賴於特定的數據庫,通過簡單的配置就可以輕松更換數據庫。通常一個orm支持很多的DEMS,如1MySQL、PostgreSQL、Oracle、SQLite等,這極大的減輕了開發人員的工作量,不需要面對因數據庫變更而導致的無效勞動。

3 如何在Flask應用ORM?

選擇ORM框架時,在我們Flask中更推薦使用Flask的擴展組件Flask-SQLchemy 。Python實現的ORM有SQLAlchemy、Peewee、PonyORM等,其中SQLAlchemy是Python社區使用最廣泛的ORM之一,Flask-SQLchemy正是基於SQLchemy。

3.1 連接數據庫:

首先切入到我們的虛擬環境 ,安裝我們的 Flask-SQLchemy

pip install flask-sqlalchemy
pip install pymysql

這里我們的DBMSmysql數據庫為例, 連接數據庫

實例

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
HOST = '127.0.0.1'
PORT = '3306'
DATABASE_NAME = '01_db'
USERNAME = 'root'
PASSWORD = 'root'

DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{databasename}?charset=utf8mb4"\
.format(username=USERNAME,password=PASSWORD,host=HOST,port=PORT,databasename=DATABASE_NAME)

app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']= False


db = SQLAlchemy(app)

解讀:

1flask_sqlalchemy模塊中導入SQLAlchemy

from flask_sqlalchemy import SQLAlchemy

2 app對象通過變量SQLALCHEMY_DATABASE_URI加載配置好的URI(統一資源標識符),URI內包含了各種用於連接數據庫的信息,指向一個具體的庫。

常用數據庫的URI格式

HOST = '127.0.0.1'  # ip
PORT = '3306'		# 端口
USERNAME = 'root'	# 數據庫賬號
PASSWORD = 'root'   # 密碼
DATABASE_NAME = '01_db'  # 具體的一個庫名
DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{databasename}?charset=utf8mb4"\
.format(username=USERNAME,password=PASSWORD,host=HOST,port=PORT,databasename=DATABASE_NAME)

app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI

3 SQLALCHEMY_TRACK_MODIFICATIONS這個配置變量決定是否追蹤對象的修改,這用於FLask- SQLALchemy的事件通知系統。這個配置鍵默認值為None,如果沒有特殊需要我們把它設置為Flase, 避免造成一些沒必要的性能浪費。

app.config['SQLALCHEMY_TRACK_MODIFICATIONS']= False

4 SQLAlchemy類傳入app類,引用app配置定位到具體的數據庫,並且實例化出db對象,這個db對象代表我們的數據庫,並且通過這個對象操作我們的ORM

db = SQLAlchemy(app)

3.2 數據庫模型

1554281734348

3.2.1 什么是數據庫模型?

繼承了db.Model的python類,並且這個python類映射到數據庫為一個表,這個python類稱之為數據庫模型。每個數據庫模型都對應着數據庫中的一個表。

3.2.2 數據庫模型實例:

class UserInfo(db.Model):
    __tablename__ = 'user_info'
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    username = db.Column(db.String(20),nullable=False)
  • __tablename__可以直接指定表名(推薦使用)。如果沒有寫__tablename__指定表名,此類名可以自動轉化為表名(不推薦使用)。
    • 類名自動轉化表名的方式為User--> user# 單個單詞轉換為小寫
      UserInfo--> user_info# 多個單詞轉換為小寫並使用下划線分隔
    • 如UserInfo類在沒有 __tablename__指定表名時候,UserInfo類會自動映射到數據庫的表名為user_info
  • db.Column類實例化表示字段(表示數據庫中的列),該類實例化出的對象被一個變量接受,該變量表示字段名。該類實例化時傳入的參數表示字段的約束。
    • 如:id = db.Column(db.Integer,primary_key=True,autoincrement=True)表示該表內id字段為主鍵並且自動增長。

1554281780499

3.2.3 常用的字段類型表:

字段 說明 映射到數據庫對應類型
Integer 整數 int類型
String 字符串,String類內可選擇length參數的值用於設置最大字符個數 varchar類型
Text 用於儲存較長的Unicode文本,,理論上可以儲存65535個字節 text類型
Date 日期,存儲Pythondatetime.date 對象 date類型
Time 時間,存儲Pythondatetime.time 對象 time類型
DateTime 時間和日期,存儲Pythondatetime 對象 datetime類型
Float 浮點類型 float類型
Double 雙精度浮點類型,比浮點類型小數位精度更高。 double類型,占據64位。
Boolean 布爾值 tinyint類型
Enum 枚舉類型 enum類型

**3.2.4 **Column常用參數表:

約束 說明
primary_key 如果設為True,該列就是表的主鍵
unique 如果設為True,該列每個值唯一,也就是該字段不允許出現重復值
index 如果設為True,為這列創建索引,用於提升查詢效率
nullable 如果設為True,這列允許使用空值,反之則不允許使用空值。
server_default 為這列定義默認值, 默認值只支持字符串,其他類型需要db.text()方法指定
default 為這列定義默認值,但是該約束並不會真正映射到表結構中,該約束只會在ORM層面實現(不推薦使用)
comment 該字段的注釋
name 可以使用該參數直接指定字段名
autoincrement 設置這個字段為自動增長的。
server_default常用配置
配置默認值類型 代碼
更新datatime時間 server_default = db.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
當前的datatime時間 server_default = db.text("CURRENT_TIMESTAMP")
數字 server_default=“數字”
布爾 server_default=db.text('True') / server_default=db.text('False')/ server_default='數字'

1554281889712

3.2.5 將寫好的模型映射到數據庫。

class UserInfo(db.Model):
    __tablename__ = 'user_info'
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    username = db.Column(db.String(20),nullable=False)
    
db.create_all()

如果你已經定義好了一個繼承db.Model的類,我們把這個類稱之為模型。我們想把這個模型映射到數據庫中,也就是在數據庫中創建這個模型所描述的一張表,使用db.create_all()可以實現把繼承了該db.model的所有模型創建到數據庫中。查看數據庫的時候我們會發現多了一張user_info表。

1549800892083

3.2.6 更新模型

如果我們要更新一個模型,並且想把這個新的模型映射到數據庫中,直接使用db.create_all()會無效,因為原來已經存在了這張表,為了解決這個問題我們可以先db.drop_all()刪除該庫下的所有繼承了db.model的模型表,然后再db.create_all()使得繼承了db.model的所有模型表映射到數據庫中,從而創建我們更新的表。這種方式的原理是先刪除數據庫中原來所有的模型表,然后在新建所有需要映射的模型表,這種方式的弊端是它把數據庫中原有的數據都銷毀了。

為了解決這種更新模型導致刪除掉原來的數據的弊端。下一章將會介紹一種更好的方式用於更新數據庫。

1554281934788

3.3 數據庫操作

3.3.1 增

模型表 映射到數據中

class School(db.Model):
    __tablename__ = "school"
    id = db.Column(db.Integer,primary_key=True,nullable=False,autoincrement=True,comment="ID")
    name = db.Column(db.String(30),nullable=False,server_default='',comment="學校名稱")
    area = db.Column(db.String(30),nullable=False,server_default='',comment="所屬地區")
    score = db.Column(db.Integer,nullable=False,server_default='600',comment="錄取分數線")
    def __repr__(self):
        return "<School(name:{})>".format(self.name)

db.create_all()

實例3.3.1.1: 新增實例

新增四條記錄映射到數據庫中

school_01 =School(name="北京大學",area="北京",score=658)  # 實例化模型類作為一條記錄
school_02 =School(name="清華大學",area="北京",score=667)
school_03 =School(name="中山大學",area="廣東",score=645)
school_04 =School(name="復旦大學",area="上海",score=650)

db.session.add(school_01)   # 把新創建的記錄添加到數據庫會話
db.session.add(school_02)
db.session.add(school_03)
db.session.add(school_04)

db.session.commit()  # 提交數據庫會話

提示:數據庫會話db.session和后面介紹的Flasksession對象沒有關系。db.session是數據庫會話也稱為事務。

  1. 實例化模型類創建對象,該對象作為一條記錄,實例化的過程傳入的參數為字段內容。
  2. 把新創建的記錄添加到數據庫會話。
  3. 提交數據庫會話

查看數據庫

1549869497823

提示1 :如果add多條記錄可以使用add_all()一次添加包含多條記錄的列表

如:db.session.add_all([school_01,school_02,school_03,school_04])

3.3.2 查

在我們的flask中db.session出的對象調用query屬性,可以通過query屬性調用各種過濾方法完成查詢。

模型類.<過濾方法>.<查詢方法>

常用過濾器表:

過濾器 說明
filter() 使用指定的規則過濾記錄相當於sql的where約束條件,返回一個新查詢
filter_by() 同filter原理,不同的是查詢的時要使用關鍵字參數,返回一個新查詢
limit() 使用指定的值限制原查詢返回的結果的數量,返回一個新查詢
offset() 偏移原查詢返回的結果,返回一個新查詢
order_by() 根據指定條件對原查詢結構進行排序,返回一個新查詢
group_by() 根據指定條件對原來查詢結構進行分組,返回一個新查詢

實例3.3.2.1: 查詢實例

下面幾個查詢案例需要在實例3.3.1完成的基礎上操作

all()返回一個列表,列表里存放所有符合條件的記錄

all_school = School.query.all()
print(all_school) 

# 輸出:[<School(name:北京大學)>, <School(name:清華大學)>, <School(name:中山大學)>, <School(name:復旦大學)>]

first()返回符合條件的第一條記錄:

school_01 =School.query.first()
print(school_01)

# 輸出:<School(name:北京大學)>

get()返回指定主鍵值(id字段)的記錄:

school_01 = School.query.get(1)
print(school_01)

#輸出:<School(name:北京大學)>

filter() 使用指定的規則過濾記錄相當於sql的where約束條件,返回新產生的查詢對象。

beijing_all = School.query.filter(School.area == "北京").all()
beijing_first = School.query.filter(School.area == "北京").first()
print(beijing_all)
print(beijing_first)

# 輸出:[<School(name:北京大學)>, <School(name:清華大學)>]
#       <School(name:北京大學)>

filter_by:同filter()效果一樣,查詢的時候使用關鍵字參數查詢(無法進行多表復雜查詢,不推薦使用)

zhongshan_school = School.query.filter_by(name='中山大學').all()
print(zhongshan_school)

# 輸出:[<School(name:中山大學)>]

db.session.qury(模型類)等價於模型類.query,db.session.qury功能更強大一些,可以進行多表查詢。

fudan_school = School.query.filter(School.name == '復旦大學').first()
print(fudan_school)  
# 輸出:<School(name:復旦大學)>

fudan_school = db.session.query(School).filter(School.name == '復旦大學').first()
print(fudan_school)
# 輸出:<School(name:復旦大學)>

提示:其他的過濾器會在接下來的章節具體根據實際案例講解

3.3.3 改

實例3.3.3.1: 修改實例

修改北京大學的錄取成績

beida = School.query.filter(School.name=='北京大學').first()
beida.score = 630
db.session.commit()

1549872535950

更新一條記錄分為一下幾部:

  1. 找到對應的記錄對象

  2. 修改記錄對象的屬性

  3. 直接調用db.session.commit()提交會話

    提示:只有要插入新的記錄或要將現有的記錄添加到會話中時才需要使用add()方法。只是更新現有記錄的時可以修改記錄對象屬性后直接提交會話

3.3.4 刪

實例3.3.4.1: 刪除實例

從數據庫中刪除清華大學相關信息

qinghua = School.query.filter(School.name=='清華大學').first()
db.session.delete(qinghua)
db.session.commit()

1549872948198

刪除一條記錄分為以下幾步:

  1. 找到對應的記錄對象
  2. 需要調用delete()方法在會話中標識需要刪除的記錄,具體是把該記錄對象傳入db.session.delete(記錄對象)實現標識。
  3. 調用db.session.commit()提交會話。

1554281987538


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM