SQLAlchemy ORM教程之一:Create
Object Relational Tutorial
所謂ORM(Object Relational Mapping),就是建立其由Python類到數據庫表的映射關系:一個Python實例(instance)對應數據庫中的一行(row)。這種映射包含兩層含義,一是實現對象和與之關聯的的行的狀態同步,二是將涉及數據庫的查詢操作,表達為Python類的相互關系。
注意ORM和SQLAlchemy的Expression Language不同。后者可以視為對原始SQL的封裝。ORM是基于Expression Language而構建的,其抽象層次要高于Expression Language。很多時候我們都是使用ORM,有時需要一些高度定制化的功能時,就需要使用到Expression Language。
下面就是教程的正文了。
Version Check
注意這里的教程都是基于1.0版本的,你可以同下面的命令來查看SQLAlchemy的版本(譯者注:如果版本號相差不大,以下教程仍然具有很強的參考意義):
>>> import sqlalchemy >>> sqlalchemy.__version___ 1.0.0Connecting
在這個教程中我們使用in-memory的SQLite數據庫,你也可以根據自己的需要配置對應的數據庫設置。為了建立同數據庫的鏈接,我們需要使用到create_engine
>>> from sqlalchemy import create_engine >>> engine = create_engine('sqlite:///:memory:', echo=True)這里的echo設置為True可以使得后面我們可以在控制臺看到操作涉及的SQL語言。如果你覺得麻煩,可以將其設置為False。我們這里就不貼出了。
create_engine返回的是一個Engine實例,它代表了指向數據庫的一些非常核心的接口。他會根據你選擇的數據庫配置而調用對應的DBAPI。
當第一次如Engine.execute()或者Engine.connect()的方法被調用時,Engine才會真正的建立起到數據庫的DBAPI連接。實際上,我們一般并不會直接使用Engine。
Declaring a Mapping
當我們使用ORM的時候,其配置過程主要分為兩個部分:一是描述我們要處理的數據庫表的信息,二是將我們的Python類映射到這些表上。這兩個過程在SQLAlchemy中是一起完成的,我們將這個過程稱之為Declarative。
使用Declarative參與ORM映射的類需要被定義成為一個指定基類的子類,這個基類應當含有ORM映射中相關的類和表的信息。這樣的基類我們稱之為declarative base class。在我們的應用中,我們一般只需要一個這樣的基類。這個基類我們可以通過declarative_base來創建
>>> from sqlalchemy.ext.declarative import declarative_base>>> Base = declarative_base()現在我們已經有了一個基類,我們可以基于這個基類來創建我們的自定義類了。我們以建立一個用戶類為例子。從Base派生一個名為User的類,在這個類里面我們可以定義將要映射到數據庫的表上的屬性(主要是表的名字,列的類型和名稱等):
>>> from sqlalchemy import Column, Integer, String >>> class User(Base): ... __tablename__ = 'users' ... ... id = Column(Integer, primary_key=True) ... name = Column(String) ... fullname = Column(String) ... password = Column(String) ... ... def __repr__(self): ... return "<User(name='%s', fullname='%s', password='%s')>" % ( ... self.name, self.fullname, self.password)通過Declarative生成的類至少應該包含一個名為tablename的屬性來給出目標表的名稱,以及至少一個Column來給出表的主鍵(Primary Key)。SQLAlchemy不會對于類名和表名之間的關聯做任何假設,也不會自動涉及數據類型以及約束的轉換。一般的你可以自己創建一個模板來建立這些自動轉換,這樣可以減少你的很多重復勞動。
當我們的類聲明完成后,Declarative將會將所有的Column成員替換成為特殊的Python訪問器(accessors),我們稱之為descriptors。這個過程我們稱為instrumentation,經過instrumentation的映射類可以讓我們能夠讀寫數據庫的表和列。
注意除了這些涉及ORM的映射意外,這些mapping類的其他部分仍然是不變的。
Create a schema
我們通過Declarative系統構建好我們的User類之后,與之同時的關于表的信息也已經創建好了,我們稱之為table metadata。描述這些信息的類為Table。我們可以通過__table__這個類變量來查看表信息
>>> User.__table__ Table('users', MetaData(bind=None),Column('id', Integer(), table=<users>, primary_key=True, nullable=False),Column('name', String(), table=<users>),Column('fullname', String(), table=<users>),Column('password', String(), table=<users>), schema=None)當我們完成類聲明時,Declarative用一個Python的metaclass來為這個類進行了加工。在這個階段,它依據我們給出的設置創建了Table對象,然后構造一個Mapper對象來與之關聯。這些幕后的對象我們大多都不需要直接與之打交道。
Table對象是一個更大家庭----我們稱之為MetaData----的一部分。當我們使用Declarative時,這個對象也可以在Declarative base class的.metadata屬性中看到。
MetaData是我們與數據庫打交道的一個接口。對于我們的SQLite數據庫而言,此時還沒有一個名為users的表的存在,我們需要使用MetaData來發出CREATE TABLE的命令。下面我們使用MetaData.create_all()指令,將我們上面得到的Engine作為參數傳入。如果你上面設置了echo為True的話,應該可以看到這一過程中的SQL指令。首先檢查了users表的存在性,如果不存在的話會執行表的創建工作。
>>> Base.metadata.create_all(engine) SELECT ... PRAGMA table_info("users") () CREATE TABLE users (id INTEGER NOT NULL, name VARCHAR,fullname VARCHAR,password VARCHAR,PRIMARY KEY (id) ) () COMMITCreate an Instance of the Mapped Class
創建User對象十分簡單
>>> ed_user = User(name='ed', fullname='Ed Jones', password='edspassword') >>> ed_user.name 'ed' >>> ed_user.password 'edspassword' >>> str(ed_user.id) 'None'Create a Session
Session是一個非常重要的概念,類似于iOS中的NSManagedContext的概念,我也在嘗試進一步去理解它。
我們現在可以和數據庫對話了。ORM對數據庫的入口即是Session,當我們構建應用時,和create_engine的同一級別下,我們定義一個Session類來作為生成新的Session的Factory類
>>> from sqlalchemy.orm import sessionmaker >>> Session = sessionmaker(bind=engine)當你試圖在定義Engine之前定義Sesssion的話,這里的bind可以不設置
>>> Session = sessionmaker()后續你定義好Engine后可以通過configure()來將其連接到Session
>>> Session.configure(bind=engine) # once engine is available這個我們自定義的工廠類就可以拿來我們構造新的Session了。
session = Session()上面的Session已經和我們的SQLite的數據庫的Engine關聯起來了,但是我們可以發現它還沒有打開任何到數據庫的連接(connection)。當一個Session被首次使用時,它會從Engine所維護的連接池中取出一個連接來操作數據庫。這個連接在我們應用有所更改或者關閉Session時會被釋放。
Adding and Update Objects
為了將User對象存入數據庫,我們調用Sesson的add()函數
>>> ed_user = User(name='ed', fullname='Ed Jones', password='edspassword') >>> session.add(ed_user)當這個操作完成之后,我們成這個User實例的狀態為pending。目前實際上還沒有執行SQL操作,也就是說數據庫中還沒有產生和這個User實例對應的行。Session將會在需要的時候執行相應的SQL命令,這個過程我們稱之為flush。如果我們試圖查詢Ed Jones,所有處于pending狀態的信息將會首先被flush,然后負責進行查詢的SQL語言在此之后立即被執行。
例如,我們創建一個查詢來獲取剛剛我們創建的用戶(涉及查詢的部分我們后續會詳細介紹)。這個查詢會返回一個和我們之前添加的用戶相同的用戶實例。
>>> our_user = session.query(User).filter_by(name='ed').first() BEGIN (implicit) INSERT INTO users (name, fullname, password) VALUES (?, ?, ?) ('ed', 'Ed Jones', 'edspassword') SELECT users.id AS users_id,users.name AS users_name,users.fullname AS users_fullname,users.password AS users_password FROM users WHERE users.name = ?LIMIT ? OFFSET ? ('ed', 1, 0) >>> our_user <User(name='ed', fullname='Ed Jones', password='edspassword')>事實上這里的Session判斷出來了需要返回的行和已經存在內存中的一個映射實例應當是同一個,所以我們會得到一個和之前完全相同的實例
>>> ed_user is our_user True這里ORM所表現的理念,我們稱之為identity map。這個設計理念保證了在一個Session對于一個制定行的操作,作用于同一個內存實例上。當一個擁有特定主鍵的對象出現在Session中時,所有的查詢操作對這個主鍵都會返回一個相同的Python對象。并且,如果你試圖引入重復了主鍵的新的對象時,系統會產生一個錯誤來阻止你的操作。
我們可以通過add_all()來一次加入多個對象
>>> session.add_all([ ... User(name='wendy', fullname='Wendy Williams', password='foobar'), ... User(name='mary', fullname='Mary Contrary', password='xxg527'), ... User(name='fred', fullname='Fred Flinstone', password='blah')])并且,如果我們希望改變Ed的密碼,可以直接修改之:
>>> ed_user.password = 'f8s7ccs'這個修改會被Session記錄下來
>>> session.dirty IdentitySet([<User(name='ed', fullname='Ed Jones', password='f8s7ccs')>])當然,上面的插入操作也被記錄了
>>> session.new IdentitySet([<User(name='wendy', fullname='Wendy Williams', password='foobar')>, <User(name='mary', fullname='Mary Contrary', password='xxg527')>, <User(name='fred', fullname='Fred Flinstone', password='blah')>])我們可以使用commit()命令來將這些更改flush到數據庫中。
>>> session.commit()總結
以上是生活随笔為你收集整理的SQLAlchemy ORM教程之一:Create的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OWASP-ZAP扫描器的使用(攻击)
- 下一篇: Windows端口占用情况?