0%

Python sqlalchemy note — 1

前言

第一次嘗試寫blog,怕不寫下來兩個星期以後都忘記了,現在還很菜,鞭小力一點。

這次的主題是python上熱門的ORM工具 — sqlalchemy

起初在寫一個有關於成績分析的sideproject,因為使用到資料庫做為儲存資料的方式,所以免不了的需要對資料庫的表格進行一些操作,原本的想法是使用 python 的 module 去做,但寫到一半發現,要是一個一個用 SQL 的語法寫下去,後面維護起來肯定是直接送急診。

於是乎就開始思考,有沒有什麼方式可以解決我的困擾。
剛好工作的關係,接觸到了一種可以方便使用者對資料庫執行操作的方式 — ORM


ORM 是什麼

ORM(Object Relational Mapping)是一種程式設計技術,用於實現物件導向程式語言裡不同類型系統的資料之間的轉換。

用最白話的方式來說,就是一個中繼站的概念,幫我們去和資料庫做溝通,我們可以用ORM的語法來撰寫程式就好。

什麼意思呢? 舉例:假設我們今天有一個 Table 叫做 Student

如果今天我們要查詢所有一年級的學生,在SQL的語法裡面我們會這樣寫

1
SELECT * FROM "Student" WHERE "grade" = 1

這樣看起來好像還好

但如果我們今天表格之間的關係開始複雜起來呢

你可能中間需要先去查詢別的表格的 id,再拿回來當作條件,同時又需要一起輸出出來,甚至還要根據使用者的條件來做篩選。可能就有機會長成這樣:

1
2
3
4
5
6
7
SELECT ("table.ele1", "table.ele2", "number", "name")
FROM "student"
inner JOIN "table"
ON "table"."id" = "student".id
WHERE a condition
AND anthor condition
AND anthor condition again

這樣看起來就麻煩了不少,不幸的是這個狀況在這個專案一定會出現而且頻率非常高。

這時就可以讓我們的ORM派上用場,省下我們寶貴的時間。

最大的優點就是直接用ORM的語法來代替上面那一串又臭又長的SQL語法,另外還可以提升安全性。

雖然我不是時間管理大師,但這聽起來很棒對吧?

你說這個東西厲害到成仙了,難道他沒有缺點嗎。
當然有,方便換來的就是效能上的犧牲。我們沒辦法一個一個用SQL的語法來最佳化查詢的效能。

但我認為在這個效能飛起的時代,這是可以接受的。


實際應用

首先我們要先建立與資料庫的連線(以下以 postgresql 為例)

1
2
3
4
5
import sqlalchemy

engine = sqlalchemy.create_engine(
'postgresql://username:password@your_ip:yourportyour_database_name', echo=True)
# 結構: 資料庫名稱://使用者名稱:密碼@資料庫ip:資料庫port/資料庫名稱

這樣就可以建立好一個連線了

如果是其他資料庫的話,可以參考官方文件

建立好連線之後我們要來定義一個新的Table

  • 這個 Table 叫做 students
  • Primary Key 是 id 且會自動生成數字
  • grade, class, number 是唯一組合的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from sqlalchemy import Column, Integer, String, Boolean  # 型別
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import UniqueConstraint # 唯一組合

Base = declarative_base()
class Student(Base):
__tablename__ = 'students' # 表格名稱
id = Column('id', Integer, primary_key=True)
name = Column('name', String(20), nullable=False) # 20為最大長度
grade = Column('grade', Integer, nullable=False)
class_ = Column('class', Integer, nullable=False)
number = Column('number', Integer, nullable=False)
UniqueConstraint(grade, class_, number, name='basic_info')
def __init__(self, name, number, grade, class_):
self.name = name
self.number = number
self.grade = grade
self.class_ = class_
def __repr__(self):
"""
回傳要顯示的資料
"""
return "<student('%s', '%d', '%d', '%d')>"%(
self.name, self.grade, self.class_, self.number)

Base.metadata.create_all(engine) # 建立所有表格

這樣我們就建立好一個 Table 了

再來我們要新增一筆資料進去

1
2
3
4
5
6
7
8
from sqlalchemy.orm import sessionmaker

value = {"name": "大偉", "grade": 1, "class_": 1, "number": 1}
Session = sessionmaker(bind=engine)
session = Session()
session.add(Students(**values))
# 也可以使用 session.add(Student("大偉", 1, 1, 1))
session.commit() # 記得儲存

你說我們有沒有快一點的方式

當然有的,我們可以改用 add_all 這個方法

1
2
3
student_value = [Student("阿明", 2, 1, 2), Student("小瑪莉", 1, 2, 3)]
session.add_all(student_value)
session.commit() # 記得儲存

最後我們來查詢資料

1
2
3
data = session.query(Student)
for i in data:
print(i)

以下是完整的程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import sqlalchemy
from sqlalchemy.orm import sessionmaker
from sqlalchemy import select
from sqlalchemy import Column, Integer, String, Boolean # 型別
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import UniqueConstraint

Base = declarative_base()
class Students(Base):
__tablename__ = 'students' # 表格名稱
id = Column('id', Integer, primary_key=True)
name = Column('name', String(20), nullable=False) # 20為最大長度
grade = Column('grade', Integer, nullable=False)
class_ = Column('class', Integer, nullable=False)
number = Column('number', Integer, nullable=False)
UniqueConstraint(grade, class_, number, name='basic_info')
def __init__(self, name, number, grade, class_):
self.name = name
self.number = number
self.grade = grade
self.class_ = class_
def __repr__(self):
"""
回傳要顯示的資料
"""
return "<student('%s', '%d', '%d', '%d')>"%(
self.name, self.grade, self.class_, self.number)

if __name__ == "__main__":
engine = sqlalchemy.create_engine(yoursql_info) # 排版問題請往上看
Session = sessionmaker(bind=engine)
session = Session()
Base.metadata.create_all(engine) # 建立 Table

value = {"name": "大偉", "grade": 1, "class_": 1, "number": 1}
session.add(Student(**values)) # 新增資料進去 Table
student_value = [Student("阿明", 2, 1, 2), Student("小瑪莉", 1, 2, 3)]
session.add_all(student_value) # 一次新增多筆
# 取得 "所有" 資料
data = session.query(Student)
for i in data:
print(i)
session.commit() # 儲存資料
session.close() # 關閉

參考資料

Wiki
官方文件


以上就是簡單的 sqlalchemy 介紹,如果有錯誤的部份,歡迎指正,謝謝。
如果你喜歡這篇文章,請幫我拍手
只需要註冊會員就可以囉,完全不用花費任何一毛錢就可以用來鼓裡創作者囉