Python SQLAlchemy --1

本文為 Python SQLAlchemy ORM 一系列教學文:

SQLAlchemy 大概是目前 Python 最完整的資料庫操作的套件了,不過最令人垢病的是它的文件真的很難閱讀,如果不搭配個實例進行學習真的很難理解。

此外,SQLAlchemy 依照架構將文件說明分為 SQLAlchemy ORMSQLAlchemy Core ,如果不去細究到底有何不同,很容易讓人誤解。

基本上,如果只是基本的資料庫的表格建立、查詢、更新、刪除等,比較不需要使用表格間的關聯以及表格與 Python 表格物件關聯的話,其實用 SQLAlchemy Core 就足以應付需求了;但是想追求優雅的解決方案,同時又需要兼顧多表格之間的關聯的話,建議使用 SQLAlchemy ORM

事實上,從 SQLAlchemy 的架構圖來看, SQLAlchemy ORM 相較於 SQLAlchemy Core 而言是高層次的功能,因此學會 SQLAlchemy ORM 的話,絕對是猶如神器在手,所向披靡。

接下來就用幾個範例實際來說明 SQLAlchemy ORM 的使用吧。

安裝

  • 本文以 Linux 環境為主
  • 需安裝 Python 3 以上
  • 需安裝 Python setuptools (連結)
  • 以 SQLAlchemy 1.0.15 為主

安裝 SQLAlchemy 很簡單,只要用 pip 指令安裝即可(不過要先用 easy_install 安裝 pip)。

$ easy_install pip
$ pip install SQLAlchemy

操作 SQLite 資料庫

SQLite 是一個極輕量級的 RDBMS(Relational DataBase Management System),非常適合較小型的非網頁架構應用程式使用,因為它不具備網路的 client/server 架構,所以無法透過網路遠端存取 SQLite 資料庫。

此外,SQLite 對於龐大的查詢、更新等負載能力也不如其他資料庫(MySQL, PostgreSQL 等)出色。即使如此,它仍具備一些十分有用的特性,例如可完全在記憶體中執行的資料庫、很多手持裝置開發環境(iOS, Android)都有支援等等,依然是一個撰寫輕量級應用程式的好選擇!

接下來,可以用範例 1 試著在記憶體中建立 SQLite 資料庫,並且新增一個 user 的表格。

範例1,連接資料庫 Engine 、新增資料表格

1234567891011121314151617181920212223242526272829303132333435363738394041
# -*- coding: utf-8 -*-import hashlibfrom sqlalchemy import create_enginefrom sqlalchemy import Column, Integer, Stringfrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy import ForeignKeyfrom sqlalchemy.orm import relationship, backref

Base = declarative_base()

class User(Base):    __tablename__ = ‘users‘

    id = Column(Integer, primary_key=True)    name = Column(String)    username = Column(String)    password = Column(String)

    def __init__(self, name, username, password):        self.name = name        self.username = username        self.password = hashlib.sha1(password).hexdigest()

    def __repr__(self):        return "User(‘{}‘,‘{}‘, ‘{}‘)".format(            self.name,            self.username,            self.password        )

if __name__ == ‘__main__‘:    ‘‘‘ 此時只有建立 SQLAlchemy Engine 實例,還沒在記憶體內建立資料,        只有第一個 SQL 指令被下達時,才會真正連接到資料庫內執行 ‘‘‘    engine = create_engine(‘sqlite:///:memory:‘, echo=True)

    ‘‘‘ 真正建立表格是使用 Base.metadata.create_all(engine) ‘‘‘    Base.metadata.create_all(engine)

    auser = User(‘user1‘, ‘username‘, ‘userpassword‘.encode(‘utf-8‘))    print(‘Mapper:‘, auser.__mapper__)

執行成功的話,應該會看到類似以下的執行訊息:

123456789101112131415
2013-08-21 13:51:39,073 INFO sqlalchemy.engine.base.Engine PRAGMA table_info("user")2013-08-21 13:51:39,073 INFO sqlalchemy.engine.base.Engine ()2013-08-21 13:51:39,074 INFO sqlalchemy.engine.base.EngineCREATE TABLE user (    id INTEGER NOT NULL,    name VARCHAR,    username VARCHAR,    password VARCHAR,    PRIMARY KEY (id))

2013-08-21 13:51:39,074 INFO sqlalchemy.engine.base.Engine ()2013-08-21 13:51:39,074 INFO sqlalchemy.engine.base.Engine COMMITMapper: Mapper|User|user

範例 1 說明

先從 35 行的 create_engine(‘sqlite:///:memory:‘, echo=True) 談起。

在 SQLAlchemy 的實做過程中,使用了Python 標準的 logging 模組進行開發,因此想要察看 SQLAlchemy 的執行過程的訊息就可以加上參數 echo=True,就可以看到 SQL 的指令與相關訊息。而 engine 是 SQLAlchemy 的 Engine 實例(instance), Engine 則是可以視為用來介接主要的資料庫(MySQL, SQLite, …)的介面。

值得注意的是,建立 Engine 實例時,實際是還沒真正連接到資料庫的,只有在第一個工作或 SQL 指令被下達,它才會真正連接到資料庫執行。

接著第 38 行,則是使用 Base.metadata.create_all(engine) 在資料庫內建立起相對應的 users 表格。這個用來建立相對應表格以及建立與 Python 類別間的動作,是由 metadata 負責的。

第 40, 41 行則是建立一個 User 類別的實例,並且列印出其所對應的 Python 類別與資料庫表格名稱,其結果為 Mapper|User|user 代表 User 類別映對到 user 資料表。此外,需要注意的是截至目前為止這筆資料仍尚未寫進資料庫內,我們會在後續的範例中學會如何將實例內的資料寫進資料庫中。

再來談談第 9 行的 declarative_base(),SQLAlchemy 使用了稱為 Declarative system 的類別,用來映對 Python 類別與資料庫表格之間的關聯,所以才會看到第 11 行 User 類別繼承了 Base ,而且又需要在 User 類別中定義 __tablename__ 屬性的值,代表它映對到資料庫中的 users 資料表。如果把 Declarative system 想像成 Python 類別介接資料庫的接線生就會相對較好理解,也因此 User 類別需要繼承 Base 才能夠讓 Declarative system 了解表格的欄位名稱、型態、長度以及相對應的 Python 類別。

通常一個應用程式也只會用到一個 declarative_base() 類別(也就是範例 1 的 Base )。

當然,SQLAlchemy 沒有這麼聰明,可以自動幫我們分別表格欄位的型態、長度、主鍵等,這些都還需要我們自行設計,這些動作也就是第 14 行到第 17 行的內容。第 14 行到第 17 行定義表格的內容,被稱為 Table metadata,而 User 類別則稱為 Mapped class ,若想知道一個實例映對的資料表名稱與 Python 類別名稱,則可以試著存取 __mapper__ 屬性,例如第 41 行。

p.s. 第 14 行到第 17 行的資料表格欄位沒有指定長度,這在 SQLite, PostgreSQL 中是合法的,被稱為 minimal table descriptiopn ,而有指定長度的情況就是 full table description

第 19 行的 __init__ 與第 24 行的 __repr__ 兩個 Python 預設類別方法。在 SQLAlchemy 這兩個方法是可以省略不寫的。如果沒有覆寫(override) __init__ 的話,SQLAlchemy 自行預設的 __init__ 就會把所有欄位列為 __init__ 的參數,因此若不改寫 __init__ 的話,預設的參數就是 id, name, username, password 4 個,而不是第 19 行所定義的 3 個(name, username, password)。

而且在建立一個資料類別的實例時,我們有時會希望對某些表格欄位的值進行檢查或是改寫,例如第 22 行的密碼加密,此時就是需要覆寫 __init__ 的情況。最後,覆寫 __repr__ 只是為了方便偵錯(debug),讓我們能夠較直觀地觀察User實例到底存放甚麼數值。

談到此處,就能夠稍微理解使用 ORM 過程就是定義資料庫表格、撰寫資料庫表格對應的 Python 類別、設定資料庫表格與其他資料庫表格間的關聯等動作。

截至目前為止,範例 1 仍未真正與資料庫進行互動,僅止於建立資料庫 Engine 、定義資料基模(scheme)。

事實上,如果要真正與資料庫進行互動(新增、刪除、修改)就得建立 Session 進行。

範例 2 將說明如何建立 Session 與資料庫進行互動。

範例 2 ,建立 Session 與資料庫互動

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
# -*- coding: utf-8 -*-import hashlibfrom sqlalchemy import create_enginefrom sqlalchemy import Column, Integer, Stringfrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy import ForeignKeyfrom sqlalchemy.orm import relationship, backreffrom sqlalchemy.orm import sessionmaker

Base = declarative_base()

class User(Base):    __tablename__ = ‘users‘

    id = Column(Integer, primary_key=True)    name = Column(String)    username = Column(String)    password = Column(String)

    def __init__(self, name, username, password):        self.name = name        self.username = username        self.password = hashlib.sha1(password).hexdigest()

    def __repr__(self):        return "User(‘{}‘,‘{}‘, ‘{}‘)".format(            self.name,            self.username,            self.password        )

if __name__ == ‘__main__‘:    engine = create_engine(‘sqlite:///:memory:‘, echo=True)    Base.metadata.create_all(engine)

    Session = sessionmaker(bind=engine)    session = Session()

    user_1 = User(‘user1‘, ‘username1‘, ‘password_1‘.encode(‘utf-8‘))    session.add(user_1)    row = session.query(User).filter_by(name=‘user1‘).first()    if row:        print(‘Found user1‘)        print(row)    else:        print(‘Can not find user1‘)

    session.rollback() # 資料庫回到新增 user1 之前的狀態

    row = session.query(User).filter_by(name=‘user1‘).first()    if row:        print(‘Found user1 after rollback‘)        print(row)    else:        print(‘Can not find user1 after rollback‘)

    user_2 = User(‘user2‘, ‘username2‘, ‘password_2‘.encode(‘utf-8‘))    session.add(user_2)    session.commit()

如果執行成功的話,會出現類似以下的結果:

12345678910111213141516171819202122232425262728293031323334
2013-08-21 17:57:32,546 INFO sqlalchemy.engine.base.Engine PRAGMA table_info("user")2013-08-21 17:57:32,547 INFO sqlalchemy.engine.base.Engine ()2013-08-21 17:57:32,548 INFO sqlalchemy.engine.base.EngineCREATE TABLE user ( id INTEGER NOT NULL, name VARCHAR, username VARCHAR, password VARCHAR, PRIMARY KEY (id))

2013-08-21 17:57:32,548 INFO sqlalchemy.engine.base.Engine ()2013-08-21 17:57:32,548 INFO sqlalchemy.engine.base.Engine COMMIT2013-08-21 17:57:32,551 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)2013-08-21 17:57:32,552 INFO sqlalchemy.engine.base.Engine INSERT INTO user (name, username, password) VALUES (?, ?, ?)2013-08-21 17:57:32,552 INFO sqlalchemy.engine.base.Engine (‘user1‘, ‘username1‘, ‘3833b3a1c69cf71a31d86cb5bb4d3866789b4d1e‘)2013-08-21 17:57:32,554 INFO sqlalchemy.engine.base.Engine SELECT user.id AS user_id, user.name AS user_name, user.username AS user_username, user.password AS user_passwordFROM userWHERE user.name = ? LIMIT ? OFFSET ?2013-08-21 17:57:32,554 INFO sqlalchemy.engine.base.Engine (‘user1‘, 1, 0)Found user12013-08-21 17:57:32,555 INFO sqlalchemy.engine.base.Engine ROLLBACK2013-08-21 17:57:32,555 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)2013-08-21 17:57:32,556 INFO sqlalchemy.engine.base.Engine SELECT user.id AS user_id, user.name AS user_name, user.username AS user_username, user.password AS user_passwordFROM userWHERE user.name = ? LIMIT ? OFFSET ?2013-08-21 17:57:32,556 INFO sqlalchemy.engine.base.Engine (‘user1‘, 1, 0)Can not find user1 after rollback2013-08-21 17:57:32,557 INFO sqlalchemy.engine.base.Engine INSERT INTO user (name, username, password) VALUES (?, ?, ?)2013-08-21 17:57:32,557 INFO sqlalchemy.engine.base.Engine (‘user2‘, ‘username2‘, ‘148dfdc3c539d35004cb808ca84e17ff962af744‘)2013-08-21 17:57:32,558 INFO sqlalchemy.engine.base.Engine COMMIT

範例 2 說明

定義類別的部份在範例 1 中已經做了不少說明,在範例 2 中就不多做說明了。

直接從第 37 行的部份開始說明,第 37 行 sessionmaker(bind=engine)engine綁定(bind)到 Session 類別中,接著我們就能在第 38 行中將這個已經與 engine 綁定的 Session 類別實例化,以開始進行與資料庫的互動。

此外, Session 類別用以下的方法進行綁定。等於是先建立一個未綁定的 Session ,稍後再將 Engine 綁定。

12
Session = sessionmaker()Session.configure(bind=engine)  # once engine is available

有了 session 實例之後,我們將 1 筆 user_1 資料交給 session 準備 加入到資料庫之中,也就是第 41 行的部份。

為甚麼是 準備 呢?因為此時的資料庫還未沒有 user_1 的資料,此時的狀態被稱為 pending (事實上,共有 4 種狀態 Transient, Pending, Persistent, Detached)。

那甚麼時候這些資料才會被新增到資料庫內呢?只有進行 QUERY, COMMIT, FLUSH 時才會被寫入資料庫內。

因此當第 42 行進行 user1 的查詢時,SQLAlchemy 會先將 user_1 的資料寫入資料庫中,再進行查詢。所以其執行結果先出現了 INSERT,接著才出現 SELECT 。

接著,第 43 行至第 47 行的部份則是用來判斷是否能夠查詢到 user_1 的資料, filter_by(name="user_1") 是對欄位名稱為 name 所下的查詢條件,first() 則是回傳查詢結果的第1筆。

事實上,使用 query() 方法時,若有查詢到結果會回傳 Query Object,若無則是回傳 None 。

然後第 49 行的部份,我們利用 session 的 rollback() 方法,將資料庫狀態回到尚未加入 user_1 時的狀態。

第 51 行到第 56 行則是再查詢一次 user_1 的資料,想當然,這筆資料將無法查詢得到,因為資料庫狀態已經回到尚未加入 user_1 時的狀態了。

最後,第 59 行則是再將 user_2 的資料加到 session 內。然後直接在第 60 行使用 session 的 commit() 方法,告訴 session 要直接將這筆資料寫入資料庫內,所以就不需等到要查詢時才會寫入到資料庫內。

到此,大家應已能夠掌握 Session 的基本操作了。

如果有需要了解更多關於 Session 的操作可以至 SQLAlchemy 中的 Session 篇察看。

參考資料:

http://www.sqlalchemy.org/

时间: 2024-10-25 04:14:00

Python SQLAlchemy --1的相关文章

python SQLAlchemy

这里我们记录几个python SQLAlchemy的使用例子: 如何对一个字段进行自增操作 user = session.query(User).with_lockmode('update').get(1)user.age += 1session.commit() 对多条数据进行删除操作: session.query(User).filter(or_(User.id == 1, User.id == 2, User.id == 3)).delete() 各种连接表,并进行多表操作: from s

Python SQLAlchemy --2

本文為 Python SQLAlchemy ORM 一系列教學文: 接下來會更深入地探討查詢的使用. 查詢的基本使用法為 session.query(Mapped Class),其後可加 .group_by(), .order_by(), .delete 等方法. 只是要注意的是 query() 接受的參數為 Mapped Class (例如先前定義的 User 類別),而不是表格名稱. 例如在 範例 2 中是以 User 做為參數,而不是以 user 這個表格名稱做為參數. 如果查詢成功就會回

Python SqlAlchemy使用方法

Python SqlAlchemy使用方法 1.初始化连接 from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine('mysql://[email protected]/test'echo=True) DBSession = sessionmaker(bind=engine) session = DBSession() ret=session.execu

记一个python+sqlalchemy+tornado的一个高并发下,产生重复记录的bug

场景:在用户通过支付通道支付完成返回时,发现我收到的处理数据记录中有两条同样的数据记录, 也就是同一笔钱,我数据库中记为了两条一样的记录. tornado端代码 from tornado import gen from tornado.concurrent import run_on_executor class processNetPay(BaseHandler): '''处理指定订单,指定支付请求,返回处理结果 ' 返回包含订单信息与用户信息体 ''' @tornado.web.asynch

Python SQLAlchemy基本操作和常用技巧包含大量实例,非常好python

http://www.makaidong.com/%E8%84%9A%E6%9C%AC%E4%B9%8B%E5%AE%B6/28053.shtml "Python SQLAlchemy基本操作和常用技巧包含大量实例,非常好python":关键词:python sqlalchemy 基本 操 作和 常用 技巧 包含 大量 实例 非常好 python 首先说下,由于最新的 0.8 版还是开发版本,因此我使用的是 0.79 版,api 也许会有些不同.因为我是搭配 mysql innodb

Python SQLAlchemy入门教程

本文将以Mysql举例,介绍sqlalchemy的基本用法.其中,Python版本为2.7,sqlalchemy版本为1.1.6. 一. 介绍 SQLAlchemy是Python中最有名的ORM工具. 关于ORM: 全称Object Relational Mapping(对象关系映射). 特点是操纵Python对象而不是SQL查询,也就是在代码层面考虑的是对象,而不是SQL,体现的是一种程序化思维,这样使得Python程序更加简洁易读. 具体的实现方式是将数据库表转换为Python类,其中数据列

Python—sqlalchemy

SQLAlchemy SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作. #Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作: ''' MySQL-Python mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname> pymysql mysql+pymysq

python sqlalchemy中commit提交错误--项非空不能插入空数据

非常白痴的问题,插入数据库提交失败,一直走IntegrityError错误,没打印错误信息,以为插不进去,弄了好久,最后打印了错误信息 (sqlite3.IntegrityError) samples.file_type may not be NULL [SQL: u'INSERT INTO samples (file_size, file_type, md5, crc32, sha1, sha256, sha512, ssdeep) VALUES (?, ?, ?, ?, ?, ?, ?, ?

Python - SQLAlchemy之连表操作

ORM的两种创建方式 数据库优先:指的是先创建数据库,包括表和字段的建立,然后根据数据库生成ORM的代码,它是先创建数据库,再创建相关程序代码 代码优先:就是先写代码,然后根据代码去生成数据库结构. 代码优先创建数据库的本质:拿到类-->转换成table对象, 然后根据table对象生成sql语句--> 生成数据库表结构 另外两个知识点: 改变数据输出的方式:可以在表的类中定义一个特殊成员:__repr__,return一个自定义的由字符串拼接的数据连接方式. 数据库中表关系之间除了MySQL