Try   HackMD

Flask實作_ext_01_Flask-SQLAlchemy_初探

tags: flask flask_ext python SQLAlchemy

說明

官方文件:

目前來說,只要在企業內要自建一個系統就一定會牽扯到對資料庫的操作,如果每一個操作都寫著一句SQL語法,那維護起來版面著實嚇人,ORM的出現幫忙程式設計師簡化了大量這類基礎SQL語法的操作,也可以減少SQL injection攻擊(SQL injection)

Flask並沒有限定一定要使用SQLAlchemy這類ORM操作的套件,但是SQLAlchemy可以搭配sqlite、Mysql、PostgreSQL、MSSql、Oracle,這已經涵蓋市面上常用的資料庫。而flask-sqlachemy是為了簡化Flask開發人員操作而出現的套件,它適度的包裝了SQLAlchemy

直觀來看,透過ORM我們可以將資料庫當做物件來操作,這讓開發人員不用花心思的去處理資料庫的DDL(Create, Drop)、DML(Select, Delete, Update, Insert..)。如果你開發過MVC那就一定很了解這一塊就是MODEL

另外,強烈建議一定要去閱讀SQLAchemy相關文件!

安裝

pip install flask-sqlalchemy

安裝flask-sqlalchemy的時候會自動下載相依套件sqlachemy,不用特別另外安裝。

實務上我們會將Model另外設置一個Python文件管理,但是這是一個單題的說明,不會這麼麻煩,只要知道有這麼一件事就可以。

範例

範例_初始化資料庫

以下是model.py文件

from flask import Flask # from flask.ext.sqlalchemy import SQLAlchemy<--新版取消了 from flask_sqlalchemy import SQLAlchemy import os # 取得目前文件資料夾路徑 pjdir = os.path.abspath(os.path.dirname(__file__)) app = Flask(__name__) # 新版本的部份預設為none,會有異常,再設置True即可。 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True # 設置sqlite檔案路徑 app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + \ os.path.join(pjdir, 'data.sqlite') db = SQLAlchemy(app) #...下略...#

利用我們在實作中的專案來簡單調整為這次的範例,sqlite是檔案式資料庫,不需要連線ip,只要知道檔案所在即可,這在android手機上很常見的應用。

既然ORM是把資料庫當物件來操作,那代表就需要建置類別,不同於sqlachemyflask-sqlachemy簡化了整個操作,在import進來的時候,它已經將sqlalchemysqlalchemy.orm通通載入並且提供了Model類別。

#...接上...# class User(db.Model): __tablename__ = 'Users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) email = db.Column(db.String(120), unique=True, nullable=False) def __init__(self, username, email): self.username = username self.email = email def __repr__(self): return '<User %r>' % self.username

第19行:繼承稍早所初始化的db.Model

完成資料庫的設置之後需要至命令模式下初始化資料庫,如下:

>>> from model_user import db
>>> db.create_all()

透過指令產生資料庫之後,就會看到在專案內多了一個data.sqlite檔案,如下圖:

範例_新增使用者

初始化資料庫之後,我們就可以驗證自己建置的ORM是否可以正常的工作,只需要在Python shell命令中去import新增的類別操作,如下:

from model_user import User # 實作類別 admin = User('admin', 'admin@abc.com') user1 = User('user1', 'user1@abc.com') # 寫入資料 db.session.add(admin) db.session.add(user1) db.session.commit()

先確認一下自己的資料庫是乾淨的:

commit之後,確實的寫入資料:

也可以確認一下自己__repr__的回傳,如下:

>>> admin <User 'admin'> >>> user1 <User 'user1'>

透過這個案例可以感受到ORM帶來的便利,我們完全沒有寫到任何一句SQL Statement就完成寫入的工作。

範例_搜尋使用者

flask-sqlalchemy內已經透過Model類(在宣告User時繼承的db.Model)提供了query的method,因此可以直接透過該類別來查詢,如下:

# 全部取回 >>> all_users = User.query.all() >>> all_users [<User 'admin'>, <User 'user1'>]

查看一下型別

>>> type(all_users) <class 'list'>

利用取回的資料找尋使用者資料

>>> all_users[1] <User 'user1'> >>> all_users[1].id 2 >>> all_users[1].username 'user1' >>> all_users[1].email 'user1@abc.com'

透過條件尋找使用者

>>> query_admin = User.query.filter_by(username='admin').first() >>> query_admin <User 'admin'>

如果沒有first?

>>> query_admin = User.query.filter_by(username='admin') >>> query_admin <flask_sqlalchemy.BaseQuery object at 0x03631E90>

範例_刪除使用者

# 透過delete刪除 >>> db.session.delete(admin) >>> db.session.commit() # commit之後查詢一下目前所有使用者 >>> user_all = User.query.all() >>> user_all [<User 'user1'>]

範例_修改使用者

# 變更使用者名稱 >>> admin.username='newAdmin' >>> db.session.add(admin) >>> db.session.commit() >>> all_user = User.query.all() >>> all_user [<User 'newAdmin'>, <User 'guest'>]

在查詢之後可直接修正屬性再透過db.session.add的方式直接做更新

總結

這是對SQLAlchemy的初探,我們所用的不過就是它的百分之一的功能,但也有前輩說過,再強大的軟體,也許常用的也只有那百分之一而以,而這百分之一也讓我們感受到ORM所帶來的便利性。

後續尚有資料庫的關聯設置以及其它QUERY的說明,記得繼續學習。

Flask-SQLAlchemy_關聯Flask實作_ext_02_Flask-SQLAlchemy_關聯
Flask-SQLAlchemy_QueryFlask實作_ext_18_Flask-SQLAlchemy_Query

延伸閱讀

管理工具

SQLite有圖形化介面可以使用,個人使用sqlitebrowser

資料庫連接方式

不同的資料庫連接有不同的相依套件需要下載,像Oracel就需要cx_Oracle,更詳細部份可參考官方文件說明。

  • MySQL
    • mysql://username:password@hostname/database
    • package: pymysql
  • Postgres
    • postgresql://username:password@hostname/database
    • package: PostgreSQL
  • SQLite(Unix)
    • sqlite:////filepath
  • SQLite(Windows)
    • sqlite:///filepath
  • Oracle
    • oracle://username:password@ip:port/databasename', echo=True
    • package: cx_Oracle

常用型別

SQLAlchemy python 說明
Integer int 整數(32bit)
SmallInteger int 整數(16bit)
BigInteger int 整數(不限精度)
Float float 浮點數
Numeric decimal.Decimal 定點數
String str 文字
Text str 長字串
Unicode unicode
UnicodeText unicode
Boolean bool
Date datetime.date
Time datetime.time
DateTime datetime.datetime
PickleType object 序列化文件
LargeBinary str 二進制文件

宣告資料物件常用參數

參數 用途
primary_key 主鍵
unique 唯一值
index 設置索引
nullable 允許null
default 設置欄位預設值

搜尋的表達式

常用過濾器

常用查詢執行函數

方法 說明
all() 回傳所有結果
first() 回傳第一個結果,沒有就回傳None
first_or_404() 應用如下說明
get() 利用指定主鍵查詢,沒有就回傳None
get_or_404() 同下說明
count() 回傳查詢結果數量
paginate() 分頁
404應用

在view中如果希望搜尋不到值的時候拋出404而不是None的話,可透過get_or_404()first_or_404()來設置,再利用app_errorhandler來做異常處理。

@app.route('/user/<username>') def get_user(username): user = User.query.filter_by(username=username).first_or_404() return render_template('Yourpage.html', user=user)