1. 程式人生 > >flask學習筆記(十三) 資料庫補充

flask學習筆記(十三) 資料庫補充

歡迎加入知了課堂,學習flask


一、資料庫

資料庫就是儲存資料的倉庫,其本質是一個檔案系統,資料按照特定的格式將資料儲存起來,使用者可以對資料庫中的資料進行增加,修改,刪除及查詢操作。通過資料庫管理系統對資料庫進行管理控制,這裡使用MySQL資料庫。MySQL中可以有多個數據庫,資料庫是真正儲存資料的地方。


資料庫中以表為組織單位儲存資料,根據表字段所規定的資料型別,我們可以向其中填入一條條的資料,叫做記錄。

下面通過Navicat for MySQL 看到的資料庫,


MySQL中可以管理多個數據庫(左圖),一個數據庫用多個數據表(右圖)。點進去其中一個數據庫表,


每一列最上面的書欄位名,代表表中的一個屬性。下面每一行代表一條記錄。

我們SQLAlchemy ORM為資料庫引擎提供抽象層,可以像使用python類一樣管理資料庫

一個類 -> 資料庫中的一張表
類中的屬性 -> 資料庫中一張表字段
類的一個物件 -> 資料庫中表的一條資料

具體方法,上一個筆記已有演示。

二、SQLAlchemy常用資料型別 & Column常用引數

  • 通過欄位給資料表中的資料新增指定資料型別

  • Column()中使用引數設定欄位

例如: id = Column(Integer,primary_key=True,autoincrement=True)

第一個引數 Integer ,指定id為整型,對映到資料庫中是int型別

primary_key:設定id欄位為主鍵

autoincrement:設定這個欄位為自動增長的

三、query查詢與filter過濾補充

在實現資料庫的增刪改查中,其中用到查詢和資料的過濾提取。

1. query可用引數:

模型物件。指定查詢這個模型中所有的物件。

articles = session.query(Article).all()
for article in articles:
print( article )

模型中的屬性。可以指定只查詢某個模型的其中幾個屬性。

articles = session.query(Article.title,Article.price).all()
print(articles)

聚合函式。

    * func.count:統計行的數量。

    * func.avg:求平均值。

    * func.max:求最大值。

    * func.min:求最小值。

    * func.sum:求和。

例如,

result = session.query(func.count(Article.id)).first()
print(result)

PS:query方法查詢返回的結果,完全取決於它要查詢的內容,也就是query後面括號裡面的東西!!!

2. filter過濾條件:

過濾是資料提取的一個很重要的功能,以下對一些常用的過濾條件進行解釋,並且這些過濾條件都是隻能通過filter方法實現的:

例如,下面實現查詢模型物件中,title欄位名與title0匹配的第一個查詢物件。

article = session.query(Article).filter(Article.title == "title0").first()
print(article)

PS:如果想要檢視orm底層轉換的sql語句,可以在filter方法後面不要再執行任何方法直接列印就可以看到了。比如,

articles = session.query(Article).filter(or_(Article.title=='abc',Article.content=='abc'))
print(articles)

四、外來鍵以及ORM關係

在關係型資料庫中,表與表之間顯然是可以有關聯的,它們通過 外來鍵 進行關聯;多表之間的關係又分為多對一、一對一、多對多。這些都是通過ORM來實現的。

1.外來鍵:

使用SQLAlchemy建立外來鍵非常簡單。在從表中增加一個欄位,指定這個欄位外來鍵的是哪個表的哪個欄位就可以了。從表中外來鍵的欄位,必須和父表的主鍵欄位型別保持一致。例如,在使用者表中使用

uid = Column(Integer,ForeignKey("user.id"))

通過外來鍵讓 uid 這個欄位和另外一個user的 id 欄位關聯了。

外來鍵約束有以下幾項: 

1. RESTRICT:父表資料被刪除,會阻止刪除。預設就是這一項。 

2. NO ACTION:在MySQL中,同RESTRICT。 

3. CASCADE:級聯刪除。 

4. SET NULL:父表資料被刪除,子表資料會設定為NULL。

  • 一對多  

mysql級別的外來鍵,還不夠ORM,必須拿到一個表的外來鍵,然後通過這個外來鍵再去另外一張表中查詢,這樣太麻煩了。SQLAlchemy提供了一個`relationship`,這個類可以定義屬性,以後在訪問相關聯的表的時候就直接可以通過屬性訪問的方式就可以訪問得到了。

class User(Base):
__tablename__ = 'user'
   
id = Column(Integer,primary_key=True,autoincrement=True)
username = Column(String(50),nullable=False)
def __repr__(self):
return "<User(username:%s)>" % self.username

class Article(Base):
__tablename__ = 'article'
   
id = Column(Integer,primary_key=True,autoincrement=True)
title = Column(String(50),nullable=False)
content = Column(Text,nullable=False)
uid = Column(Integer,ForeignKey("user.id"))

author = relationship("User",backref="articles")

另外,可以通過`backref`來指定反向訪問的屬性名稱。articles是有多個。他們之間的關係是一個一對多的關係。

__repr__在python中是一個魔術方法,在這裡的作用是,一旦使用print列印User物件時,會通過這個方法返回一個可以用來表示物件的可列印字串

  • 一對一

在sqlalchemy中,如果想要將兩個模型對映成一對一的關係,那麼應該在父模型中,指定引用的時候,要傳遞一個`uselist=False`這個引數進去。就是告訴父模型,以後引用這個從模型的時候,不再是一個列表了,而是一個物件了。

extend = relationship( "UserExtend", uselist=False )
  • 多對多

具體操作流程:

1. 多對多的關係需要通過一張中間表來繫結他們之間的關係。

2. 先把兩個需要做多對多的模型定義出來

3. 使用Table定義一箇中間表,中間表一般就是包含兩個模型的外來鍵欄位就可以了,並且讓他們兩個來作為一個“複合主鍵”。

4. 在兩個需要做多對多的模型中隨便選擇一個模型,定義一個relationship屬性,來繫結三者之間的關係,在使用relationship的時候,需要傳入一個secondary=中間表。

五、ORM層面的刪除資料

ORM層面刪除資料,會無視mysql級別的外來鍵約束。直接會將對應的資料刪除,然後將從表中的那個外來鍵設定為NULL。如果想要避免這種行為,應該將從表中的外來鍵的`nullable=False`。

六、排序

可以通過sqlalchemy對提取出來的資料,在一定條件下進行排序,

1.在模型定義的時候指定預設排序:有些時候,不想每次在查詢的時候都指定排序的方式,可以在定義模型的時候就指定排序的方式。有以下兩種方式:

    * relationship的order_by引數:在指定relationship的時候,傳遞order_by引數來指定排序的欄位。

author = relationship("User",backref=backref("articles",order_by=create_time.desc()))

    * 在模型定義中,新增以下程式碼:

__mapper_args__ = {
"order_by": create_time
}

使用示例

user = session.query(User).first()
print(user.articles)

直接打印出已經排序好的資料列。

2.正序排序與倒序排序:預設是使用正序排序。如果需要使用倒序排序,那麼可以使用這個欄位的`desc()`方法,或者是在排序的時候使用這個欄位的字串名字,然後在前面加一個負號。

articles = session.query(Article).order_by(Article.id.desc())

七、limit、offset和切片操作

就像python中對列表的切片一樣,

1. limit:可以限制每次查詢的時候只查詢幾條資料。

2. offset:可以限制查詢資料的時候過濾掉前面多少條。

3. 切片:可以對Query物件使用切片操作,來獲取想要的資料。可以使用`slice(start,stop)`方法來做切片操作。也可以使用`[start:stop]`的方式來進行切片操作。一般在實際開發中,中括號的形式是用得比較多的。

articles = session.query(Article).order_by(Article.id.desc())[0:10]
print(articles)

八、懶載入

在一對多,或者多對多的時候,如果想要獲取多的這一部分的資料的時候,往往能通過一個屬性就可以全部獲取了。比如有一個作者,想要或者這個作者的所有文章,那麼可以通過user.articles就可以獲取所有的。但有時候我們不想獲取所有的資料,比如只想獲取這個作者今天發表的文章,那麼這時候我們可以給relationship傳遞一個lazy='dynamic',以後通過user.articles獲取到的就不是一個列表,而是一個AppenderQuery物件了。這樣就可以對這個物件再進行一層過濾和排序等操作。

通過`lazy='dynamic'`,獲取出來的多的那一部分的資料,就是一個`AppenderQuery`物件了。這種物件既可以新增新資料,也可以跟`Query`一樣,可以再進行一層過濾。

總而言之一句話:如果你在獲取資料的時候,想要對多的那一邊的資料再進行一層過濾,那麼這時候就可以考慮使用`lazy='dynamic'`。

九、group_by分組與having再次過濾

1.根據某個欄位進行分組。比如想要根據性別進行分組,來統計每個分組分別有多少人,那麼可以使用以下程式碼來完成:

session.query(User.gender,func.count(User.id)).group_by(User.gender).all()

2.having是對查詢結果進一步過濾。比如只想要看未成年人的數量,那麼可以首先對年齡進行分組統計人數,然後再對分組進行having過濾。示例程式碼如下:

result = session.query(User.age,func.count(User.id)).group_by(User.age).having(User.age >= 18).all()

十、join進行資料庫連線

join分為left join(左外連線)和right join(右外連線)以及內連線(等值連線)。

參考的網頁:http://www.jb51.net/article/15386.html

在sqlalchemy中,使用join來完成內連線。在寫join的時候,如果不寫join的條件,那麼預設將使用外來鍵來作為條件連線。

比如現在要實現一個功能,要查詢所有使用者,按照發表文章的數量來進行排序。示例程式碼如下:

result = session.query(User,func.count(Article.id)).join(Article).
group_by(User.id).order_by(func.count(Article.id).desc()).all()


十一、subquery:子查詢

子查詢可以讓多個查詢變成一個查詢,只要查詢一次資料庫,效能相對來講更加高效一點。不用寫多個sql語句就可以實現一些複雜的查詢。那麼在sqlalchemy中,要實現一個子查詢,應該使用以下幾個步驟:

1. 將子查詢按照傳統的方式寫好查詢程式碼,然後在`query`物件後面執行`subquery`方法,將這個查詢變成一個子查詢。

2. 在子查詢中,將以後需要用到的欄位通過`label`方法,取個別名。

3. 在父查詢中,如果想要使用子查詢的欄位,那麼可以通過子查詢的返回值上的`c`屬性拿到。

stmt = session.query(User.city.label("city"),User.age.label("age")).filter(User.username=='李A').subquery()
result = session.query(User).filter(User.city==stmt.c.city,User.age==stmt.c.age).all()
print(result)

在資料庫中尋找和李A在同一城市同一個年齡的人,列印結果!

重要參考:知了課堂Flask課堂筆記

歡迎加入知了課堂,學習flask