Python全棧工程師之從網頁搭建入門到Flask全棧專案實戰(5) - Flask中的ORM使用

葛老頭發表於2022-12-08

1.理解ORM

  • ORM是MTV模型裡面的Model模型
  • ORM(Object Relational Mapping),物件關係對映
  • 舉例:學生選課

學生和課程這兩個實體,一個學生可以選擇多門課程,一個課程可以被多名學生選擇。這兩個實體是多對多的關係,學生選課對應的資料庫表

為什麼要學習ORM

ORM的重要特性

  • 物件導向的程式設計思想,方便擴充
  • 少寫(幾乎不寫)SQL,提升開發效率
  • 支援多種型別的資料庫,方便切換
  • ORM技術成熟,能解決絕大部分問題

 

2.環境安裝

2.1.Flask-sqlalchemy介紹及安裝

  • PIP安裝: pip install -U Flask-SQLAlchemy 
  • 原始碼安裝: python setup.py install 
  • 使用國內映象安裝: pip install -U -i https://mirrors.aliyun.com/pypi/simple flask-sqlalchemy 

常見安裝報錯:ERROR: Could not install packages due to an OSError:

解決方案:在pip install 後面加上 --user即可

 

備註:如果是第一次安裝flask-sqlalchemy,還需要安裝它資料庫的依賴mysqlclient。 pip install mysqlclient 

安裝mysqlclient常見報錯: error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": https://visualstudio.microsoft.com/downloads/

 解決方案1:https://blog.csdn.net/alicee_2012/article/details/122726986

解決方案2:https://blog.csdn.net/weixin_42403632/article/details/117087559(我的報錯是透過這個解決的)

 安裝成功?

 

2.2.Flask-sqlalchemy配置

3.ORM的CURD操作

3.1.設計資料庫模型並建立表

 

資料庫模型設計

  • 1)繫結到Flask物件: db=SQLAlchemy(app) 
  • 2)ORM模型建立:
    Python全棧工程師之從網頁搭建入門到Flask全棧專案實戰(5) - Flask中的ORM使用
    class User(db.Model):
        id=db.Column(db.Integer,primary_key=True)
    View Code
  • 3)指定表的名稱: __tablename__='weibo_user' 

 

建立和刪除表

  • 1)手動建立資料庫
  • 2)建立表: >>> db.create_all(bind='db1') #bind='db1'可以不傳,當存在需要對多個資料庫建立表時,需要新增對應的資料庫物件。只有一個庫時,不用加 
  • 3)刪除表: >>> db.drop_all() 

 

例項驗證:設計資料庫模型,建立/刪除表

建立指定名稱的表

3.2.資料庫模型設計

 

資料庫的表肯定不止一張,同時資料表之間都會有一些關聯關係,該如何實現?

  • 透過db.ForeignKey()進行外來鍵關聯
  • 透過db.relationship()進行反向引用

 1 from flask import Flask, render_template
 2 from flask_sqlalchemy import SQLAlchemy
 3 
 4 app = Flask(__name__)
 5 # 配置資料庫的連線引數
 6 app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:*****@*******/test_flask'
 7 
 8 db = SQLAlchemy(app)
 9 
10 
11 class User(db.Model):   #User為模型名稱,即類名為模型名稱
12     """ 使用者的基礎資訊 """
13     __tablename__ = 'weibo_user'
14     id = db.Column(db.Integer, primary_key=True)
15     username = db.Column(db.String(64), nullable=False)
16     password = db.Column(db.String(256), nullable=False)
17     birth_date = db.Column(db.Date, nullable=True)
18     age = db.Column(db.Integer, default=0)
19 
20 class UserAddress(db.Model):
21     """ 使用者的地址 """
22     __tablename__ = 'weibo_user_addr'
23     id = db.Column(db.Integer, primary_key=True)
24     addr = db.Column(db.String(256), nullable=False)
25     user_id = db.Column(db.Integer, db.ForeignKey('weibo_user.id'), nullable=False)   #使用db.ForeignKey()進行外來鍵關聯
26     user = db.relationship('User', backref=db.backref('address', lazy=True))      #這一行程式碼不會在weibo_user_addr這張表裡面增加一列,它的作用就是為了方便我們操作模型的時候去訪問去查詢
27     # 使用db.relationship()進行反向引用。反向關聯誰呢,關聯User模型;建立一個反向引用backref=db.backref(),反向引用給誰呢,給address這個名字自己定義,lazy=True就是我們查詢到我們使用者物件的時候,不直接把他下面所有的地址列表給他查出來,當我們去訪問的時候,訪問下面地址列表的時候再給他查出來
28 
29 '''
30 接著上面程式碼舉個例子進一步解釋說明反向引用:
31 UserAddress裡面有個反向引用,引用的誰呢,引用的User
32 
33 場景一:
34 user = User()   #得到某一個使用者張三,想知道張三下面所有得地址列表怎麼辦?
35 user.address    #透過user.address就可以獲取到到UserAddress這個模型了,就可以找到張三下面得所有地址列表,即地址表內容
36 
37 場景二:
38 假設我們查到了某一個地址,也就是UserAddress模型對應資料庫的內容
39 addr = UserAddress()   #根據地址如何去查使用者表的資訊呢?
40 addr.user              #透過addr.user 就得到了User模型的物件,也就是user表物件
41 '''
42 
43 
44 
45 @app.route('/')
46 def mine():
47     """  首頁 """
48     return render_template('index.html')

 

3.3.使用ORM插入、修改、刪除資料

繼續上面程式碼和使用者表、地址表闡述

 

新增/修改資料

  • 構造ORM模型物件: user = User('admin','admin@example.com') 。在模型類User裡面傳遞一些引數:使用者名稱、密碼、使用者id等等即user表資料內容,構造一個user物件
  • 新增到db.session,session可以理解為一個會話(備註:session裡面可新增多個物件): db.session.add(user) ,可以多次呼叫add傳遞多個物件
  • 提交到資料庫: db.session.commit ,透過commit就完成了在使用者表裡面新增一條使用者記錄

 

新增資料例項:

修改資料例項:

 

物理刪除資料:資料記錄在表中直接刪掉

  • 透過query.filter_by()查詢ORM模型物件: user=User.query.filter_by(username='王五').first() ,透過query.filter_by找到User模型中使用者叫王五的資料,first取第一條
  • 新增到db.session,注意這個地方是delete不是add: db.session.delete(user) 
  • 提交變更到資料庫: db.session.commit

 

物理刪除資料例項:

 

邏輯刪除:相當於軟刪除,在表中新增一個狀態位,比如isvalid欄位,用於標記該記錄是否刪除(0:刪除,1:未刪除),其實表中資料記錄還在的。

 

3.4.使用ORM進行資料查詢與展示

繼續上面程式碼和使用者表、地址表闡述

ORM查詢

ORM查詢返回的是一個結果集,可以把它看成list。注意:當模型沒有使用__tablename__命名錶名,那麼表名就是模型名即類的名字。

 

篩選/獲取ORM返回結果集:

  • 查詢模型(表)所有資料query.all(): User.query.all() 
  • 按條件查詢
    • query.filter_by(): User.query.filter_by(username='張三') 
    • query.filter(): User.query.filter(User.nickname.endswith('')).all() 
    • 可以使用filter()進行一些複雜的查詢,:如nickname.endswith() 表中nickname欄位是什麼結尾的、上面all()是將查詢出來的結果全部返回。

 

對ORM返回結果進行操作:

  • 排序query.order_by(): User.query.order_by(User.username) 
  • 查詢TOP10的資料 query.limit(): User.query.limit(10).all() 

 

返回單個ORM物件即查詢結果只有一條資料

  • 根據主鍵(primary_key)值查詢: User.query.get(1) 
  • 獲取第一條記錄first(): User.query.first() 

 

同時還有一些常用的檢視快捷函式:

  • 返回的資料有則返回,無則返回404
    • first()  vs  first_or_404()
    • get()   vs  get_or_404()

  •  多表關聯查詢
    • 方式一:db.session.query(User).join(Address)
    • 方式二:User.query.join(Address)
  • 分頁(offset/limit)
    • 方式一:.offset(offset)
    • 方式二:.limit(limit)
    • offset和limit使用方法和在mysql中使用一致
  • 分頁(paginate):query物件提供的paginate函式進行分頁。 .paginate(page=2,per_page=4) #page當前在第幾頁,per_page每一頁多少條,返回的是一個Pagination的物件 。返回的Pagination物件擁有的屬性和函式如下?
    •  has_prev/has_next :是否有上一頁/下一頁
    •  items :當前頁的資料列表
    •  prev_num/next_num :上一頁/下一頁的頁碼
    •  total :總記錄數
    •  pages :總頁數

 

paginate例項練習:

1)準備資料

2)分頁: list_user.paginate(page2,per_page=4) 

3)在模板中實現分頁操作

 1 '''***app.py***'''
 2 
 3 from flask import Flask, render_template
 4 from flask_sqlalchemy import SQLAlchemy
 5 
 6 app = Flask(__name__)
 7 # 配置資料庫的連線引數
 8 app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:**@******/test_flask'
 9 db = SQLAlchemy(app)
10 
11 
12 class User(db.Model):
13     __tablename__ = 'weibo_user'
14     id = db.Column(db.Integer, primary_key=True)
15     username = db.Column(db.String(64), nullable=False)
16     password = db.Column(db.String(256), nullable=False)
17     birth_date = db.Column(db.Date, nullable=True)
18     age = db.Column(db.Integer, default=0)
19 
20 
21 class UserAddress(db.Model):
22     """ 使用者的地址 """
23     __tablename__ = 'weibo_user_addr'
24     id = db.Column(db.Integer, primary_key=True)
25     addr = db.Column(db.String(256), nullable=False)
26     user_id = db.Column(db.Integer, db.ForeignKey('weibo_user.id'), nullable=False)
27     user = db.relationship('User', backref=db.backref('address', lazy=True))
28 
29 
30 @app.route('/')
31 def mine():
32     """  首頁 """
33     return render_template('index.html')
34 
35 
36 @app.route('/user/<int:page>/')
37 def list_user(page):
38     """ 使用者分頁 """
39     per_page = 10 # 每一頁的資料大小
40     # 1. 查詢使用者資訊
41     user_ls = User.query
42     # 2. 準備分頁的資料
43     user_page_data = user_ls.paginate(page, per_page=per_page)
44     return render_template('list_user.html', user_page_data=user_page_data)
 1 <!--  ***list_user.html***   -->
 2 
 3 <!DOCTYPE html>
 4 <html lang="en">
 5 <head>
 6     <meta charset="UTF-8">
 7     <title>使用者分頁操作</title>
 8 </head>
 9 <body>
10 <h3>總共有{{ user_page_data.total }}使用者,當前在第{{ user_page_data.page }}頁使用者, 總共{{ user_page_data.pages }}頁</h3>
11 <p>
12     使用者列表:
13 
14 <ul>
15     {% for user in user_page_data.items %}
16     <li>{{ user.username }} - {{ user.password }}</li>
17     {% endfor %}
18 </ul>
19 {% if user_page_data.has_prev %}
20 <a href="{{ url_for('list_user', page=user_page_data.prev_num) }}">上一頁</a>
21 {%  endif %}
22 {% if user_page_data.has_next %}
23 <a href="{{ url_for('list_user', page=user_page_data.next_num) }}">下一頁</a>
24 {% endif %}
25 </p>
26 </body>
27 </html>

思考:

透過本篇ORM學習,實現了可以將模型資料透過檢視傳遞給模板進行展示。那麼模板html如何透過檢視對模型中的資料進行增刪改查?詳見下一篇筆記:Flask表單的實現

 

相關文章