ORM: Object Relational Mapper.html
目前Python有不少ORM工具能夠將數據庫映像爲Python的Objects對象。
其中比較知名的有Django的ORM,SQLAlchemy, PostgreSQL等。SQLAlchemy
有更多的人維護,功能也比較齊全。因此通常是咱們的首選項。python
對於SQLAlchemy
的使用者來講,只要你一開始鏈接上數據庫,不論是Sqlite,MySQL仍是什麼,後面的處理方式徹底同樣。這種便利性也是它受歡迎的緣由。mysql
拋棄了傳統的本身編織SQL語句、製做模型、鏈接數據庫方式,SQLAlchemy
直接把這些東西全包在黑盒裏面,讓咱們徹底不須要去管。連SQL-Injection注入這種東西也被它幫忙防範了。這樣一來,能夠說在鏈接數據庫方面,幫咱們節省了最少一半以上的代碼。sql
甚至連數據查詢,SQLAlchemy也代替了SQL語句,而使用了專門的相似MongoDB的Object.query.filter_by(name='Jason').all()
這種方法。數據庫
安裝:segmentfault
# 安裝sqlalchemy $ pip install sqlalchemy
安裝Drivers:session
# Sqlite # 不須要,Python自帶 # MySQL $ pip install pymysql # Postgresql $ pip install psycopg2
SQLAlchemy自身不帶數據庫driver,須要咱們本身安裝,並在鏈接時候指定。
而這些driver,實際上就是咱們曾經手動鏈接數據庫所用的包。而SQLAlchemy只是代替咱們使用這些一樣的包。app
建立一個sqlite的ORM引擎:工具
from sqlalchemy import create_engine # 鏈接格式爲:sqlite://<Hostname>/<path> engine = create_engine('sqlite:///foo.db', echo=True)
建立一個MySQL的ORM引擎:測試
from sqlalchemy import create_engine # 鏈接格式爲:dialect+driver://username:password@host:port/database engine = create_engine('mysql+pymysql://root:password123@localhost/db_test_01', echo=True)
數據庫的位置(三斜槓爲相對路徑,四斜槓爲絕對路徑):
# 使用絕對路徑的數據庫文件(////),如/tmp/mydatabase.db engine = create_engine('sqlite:////tmp/mydatabase.db') # 使用當前「執行位置」數據庫文件(///或///./) engine = create_engine('sqlite:///mydatabase.db') # 使用當前「執行位置」父級目錄(///../)的數據庫文件 engine = create_engine('sqlite:///../mydatabase.db') # 使用當前「腳本位置」的數據庫文件 import os cwd = os.path.split(os.path.realpath(__file__))[0] engine = create_engine('sqlite:///{}/mydatabase.db'.format(cwd))
注意:不一樣於SQL語句,SQLAlchemy中的表名是 徹底區分大小寫的!
建立一個Schema表(指單純表,不包含ORM對象):
from sqlalchemy import create_engine, MetaData from sqlalchemy import Table, Column from sqlalchemy import Integer, String, ForeignKey engine = create_engine('mysql+pymysql://root:password123@localhost/db_test_01', echo=True) metadata = MetaData(engine) # 建立一個表 user_table = Table( 'tb_user', metadata, Column('id', Integer, primary_key=True), Column('name', String(50)), Column('fullname', String(100)) ) # 讓改動生效 metadata.create_all()
建立一個ORM對象(包括表):
# 導入表格建立引擎 from sqlalchemy import create_engine # 導入列格式 from sqlalchemy import Column, Integer, String, ForeignKey # 導入建立ORM模型相關 from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class User(Base): __tablename__ = 'tb_Person' id = Column('id', Integer, primary_key=True) username = Column('username', String, unique=True) engine = create_engine('sqlite:///test.sqlite', echo=True) Base.metadata.create_all(bind=engine)
用普通表Table和ORM對象建立的表有什麼不一樣?
他們在數據庫中建立的,是徹底相同的表!惟一區別是,Table建立的不包含ORM對象,也就是不提供讓你直接操做Python對象的功能。
這麼作的好處是,有不少只是關聯做用的表,沒有必要生成ORM對象。
# engine = ... # Base = ... # 逐個ORM對象刪除對應的表,如User類 User.__table__.drop(engine) # 刪除所有表 Base.metadata.drop_all(engine)
設計或調試過程當中,咱們常常要頻繁改動表格,因此有必要在建立表格前把測試數據庫中的表都清除掉,再建立新的定義。
將數據添加到數據庫:
# ... # 導入session相關(用於添加數據) from sqlalchemy.orm import sessionmaker, relationship user = User() user.id = 1 user.username = 'Jason' Session = sessionmaker(bind=engine) session = Session() session.add(user) session.commit() session.close()
注意:這裏的session
和網站上的session概念有點不同。這裏是用來commit提交數據庫變更的工具。
批量添加數據(向add_all()傳入列表):
session.add_all( [user1, user2, user3] )
添加每條數據的時候自動flush():
session = sessionmaker(bind=engine, autoflush=True)
autoflush
是在每次session.add()
自動執行session.flush()
,即在插入數據庫以前就在內存中生成全部對象的動態數據(如主鍵ID等)。通常默認是選false,由於會影響效率。最好是須要的時候,才手動執行session.flush()
具體原因,看下一節「數據生效」。
SQLAlchemy中的create_all()
和session.commit()
都是直接讓python文件中定義的對象在數據庫中生效的語句。在此以前,不管怎麼定義,數據都是在內存中,而沒有在數據庫中的。
注意區分:
create_all
只是讓建立表格結構生效,無關insert的數據條目session.commit()
只是讓添加的數據生效,而不負責任何表格結構。這兩個的順序,固然是先建立表格,再插入數據。
只是,若是咱們知道了這個原理,在編碼中才能比較運用自由。好比,連create_engine()
建立引擎,咱們均可以在後面定義,而不必非得寫在文件頭,即全部的ORM定義以前。create_engine
只要定義在全部ORM類和Schema表以後便可。
此後,咱們再開始進行數據插入工做,也就利用到了session。
session過程當中呢,咱們也會遇到互相引用主鍵外鍵ID的狀況。可是注意,這時候由於尚未使用最終的session.commit()
真正提交數據到數據庫中,這些ID是沒有值的。
解決辦法就是利用內置的方法session.flush()
,將session中已添加的全部對象填充好數據,可是這時候尚未提交到數據庫,只是咱們內部能夠正常訪問各類ID了。
更新:
# Get a row of data me = session.query(User).filter_by(username='Jason').first() # Method 1: me.age += 1 session.commit() # Method 2: session.query().filter( User.username == 'Jason' ).update( {"age": (User.age +1)} ) session.commit() # Method 3: setattr(user, 'age', user.age+1) session.commit()
#sqlalchemy can't get primary key
, #sqlalchemy 如何得到主鍵的值
這個問題花了我不少時間探索查詢,不得其解,才明白原來是很顯然的事。
雖然在沒有用session或engine插入數據以前,咱們能夠直接瀏覽從ORM建立的對象中的屬性值。
可是這個時候不管如何都獲取不到primar_key
主鍵列的值。
由於這時候主鍵尚未插入數據庫,做爲動態的值
,在數據庫沒生效以前也就爲None。
爲何須要獲取value of primary_key
?考慮以下這些場景:
foreign key
外鍵須要引用主表的id那麼該怎麼獲取主鍵ID呢?
再參考Stackoverflow:sqlalchemy flush() and get inserted id?
再參考:sqlalchemy獲取插入的id
再參考:Sqlalchemy;將主鍵設置爲預先存在的數據庫表(不使用sqlite)
若是要想在插入數據以前就獲取主鍵等動態列
的值,那麼有這幾種方法:
session.add(..)
,再session.flush()
,而後就能夠獲取ID,最後再session.commit()
推薦作法以下:
即每次新建立對象後,馬上session.add(..),而後馬上session.flush(),所有都添加好的文末,再session.commit().
注意:query是經過session進行的,也就是必須在session.commit()
以後才能進行查詢,不然會報錯。
這裏將的query查詢,指的都是
在插入到數據庫生效以後
。理解這個很重要,由於在對象未插入到數據庫以前,不少主鍵、外鍵等內容都是不存在的,也就沒法查詢到。
參考:pythonsheets - Object Relational basic query
查詢數據:
session.commit() # ... users = session.query(User).all() # 返回的是多個User類的對象:>>> [ <User 1>, <User 2>, .... ] for u in users: print(u.id, u.username)
經常使用查詢方法:
# 獲取某ORM中數據 .query(ORM類名) >>> session.query( User ).all() # All rows of data >>> session.query( User ).first() # First row of data as an object # 查詢結果排序 .order_by(類名.列名) >>> session.query(User).order_by( User.birth ).all() # 篩選結果 .filter( True/False 表達式 ) >>> session.query(User).filter( User.name != 'Jason' ).all() >>> session.query(User).filter( User.name.like('%ed%') ).all() # Fuzzy search >>> session.query(User).filter( User.id in [1, 2, 3] ).all() # IN >>> session.query(User).filter( ~ User.id in [4, 5, 6] ).all() # NOT IN >>> session.query(User).filter( User.school == 'MIT', User.age < 24 ).first() # AND >>> session.query(User).filter( _or(User.school == 'MIT', User.age < 24) ).first() # OR