Python3+SQLAlchemy+Sqlite3實現ORM教程

1、安裝

Sqlite3是Python3標準庫不須要另外安裝,只須要安裝SQLAlchemy便可。本文sqlalchemy版本爲1.2.12html

pip install sqlalchemy

 

2、ORM操做

除了第一步建立引擎時鏈接URL不同,其餘操做其餘mysql等數據庫和sqlite都是差很少的。python

 

2.1 建立數據庫鏈接格式說明

sqlite建立數據庫鏈接就是建立數據庫,而其餘mysql等應該是須要數據庫已存在才能建立數據庫鏈接;創建數據庫鏈接本文中有時會稱爲創建數據庫引擎。mysql

 

2.1.1 sqlite建立數據庫鏈接

以相對路徑形式,在當前目錄下建立數據庫格式以下:sql

# sqlite://<nohostname>/<path>
# where <path> is relative:
engine = create_engine('sqlite:///foo.db')

以絕對路徑形式建立數據庫,格式以下:數據庫

#Unix/Mac - 4 initial slashes in total
engine = create_engine('sqlite:////absolute/path/to/foo.db')
#Windows
engine = create_engine('sqlite:///C:\\path\\to\\foo.db')
#Windows alternative using raw string
engine = create_engine(r'sqlite:///C:\path\to\foo.db')

sqlite能夠建立內存數據庫(其餘數據庫不能夠),格式以下:flask

# format 1
engine = create_engine('sqlite://')
# format 2
engine = create_engine('sqlite:///:memory:', echo=True)

 

2.1.2 其餘數據庫建立數據庫鏈接

PostgreSQL:session

# default
engine = create_engine('postgresql://scott:tiger@localhost/mydatabase')
# psycopg2
engine = create_engine('postgresql+psycopg2://scott:tiger@localhost/mydatabase')
# pg8000
engine = create_engine('postgresql+pg8000://scott:tiger@localhost/mydatabase')

MySQL:多線程

# default
engine = create_engine('mysql://scott:tiger@localhost/foo')
# mysql-python
engine = create_engine('mysql+mysqldb://scott:tiger@localhost/foo')
# MySQL-connector-python
engine = create_engine('mysql+mysqlconnector://scott:tiger@localhost/foo')
# OurSQL
engine = create_engine('mysql+oursql://scott:tiger@localhost/foo')

Oracle:oracle

engine = create_engine('oracle://scott:tiger@127.0.0.1:1521/sidname')

engine = create_engine('oracle+cx_oracle://scott:tiger@tnsname')

MSSQL:框架

# pyodbc
engine = create_engine('mssql+pyodbc://scott:tiger@mydsn')
# pymssql
engine = create_engine('mssql+pymssql://scott:tiger@hostname:port/dbname')

 

2.2 建立數據庫鏈接

咱們以在當前目錄下建立foo.db爲例,後續各步同使用此數據庫。

在create_engine中咱們多加了兩樣東西,一個是echo=Ture,一個是check_same_thread=False。

echo=Ture----echo默認爲False,表示不打印執行的SQL語句等較詳細的執行信息,改成Ture表示讓其打印。

check_same_thread=False----sqlite默認創建的對象只能讓創建該對象的線程使用,而sqlalchemy是多線程的因此咱們須要指定check_same_thread=False來讓創建的對象任意線程均可使用。不然不時就會報錯:sqlalchemy.exc.ProgrammingError: (sqlite3.ProgrammingError) SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 35608 and this is thread id 34024. [SQL: 'SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password \nFROM users \nWHERE users.name = ?\n LIMIT ? OFFSET ?'] [parameters: [{}]] (Background on this error at: http://sqlalche.me/e/f405)

from sqlalchemy import create_engine

engine = create_engine('sqlite:///foo.db?check_same_thread=False', echo=True)

 

2.3 定義映射

先創建基本映射類,後邊真正的映射類都要繼承它

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

而後建立真正的映射類,咱們這裏以一下User映射類爲例,咱們設置它映射到users表。ORM中表是不須要先存在的,反而是後續要經過映射類來建立表,這一點是須要明確的。

from sqlalchemy import Column, Integer, String

# 定義映射類User,其繼承上一步建立的Base
class User(Base):
    # 指定本類映射到users表
    __tablename__ = 'users'
    
    # 指定id映射到id字段; id字段爲整型,爲主鍵
    id = Column(Integer, primary_key=True)
    # 指定name映射到name字段; name字段爲字符串類形,
    name = Column(String(20))
    fullname = Column(String(32))
    password = Column(String(32))

    def __repr__(self):
        return "<User(name='%s', fullname='%s', password='%s')>" % (
                   self.name, self.fullname, self.password)

 

2.4 建立數據表

# 查看映射對應的表
User.__table__

# 建立數據表
Base.metadata.create_all(engine)

 

 

2.5 創建會話

增查改刪(CRUD)操做須要使用session進行操做

from sqlalchemy.orm import sessionmaker

# engine是2.2中建立的鏈接
Session = sessionmaker(bind=engine)

# 建立Session類實例
session = Session()

 

 

2.6 增(向users表中插入記錄)

# 建立User類實例
ed_user = User(name='ed', fullname='Ed Jones', password='edspassword')

# 將該實例插入到users表
session.add(ed_user)

# 一次插入多條記錄形式
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')]
)

# 當前更改只是在session中,須要使用commit確認更改纔會寫入數據庫
session.commit()

 

2.7 查(查詢users表中的記錄)

2.7.1 查實現

query至關前select xxx from xxx部分。

filter_by至關於where部分,外另可用filter。他們的區別是filter_by參數爲sql形式,filter參數爲python形式。

# 指定User類查詢users表,查找name爲'ed'的第一條數據
our_user = session.query(User).filter_by(name='ed').first()

our_user

# 比較ed_user與查詢到的our_user是否爲同一條記錄
ed_user is our_user

更多查詢語句見:https://docs.sqlalchemy.org/en/latest/orm/tutorial.html#querying

不過要注意該連接Common Filter Operators節中形如equals的query.filter(User.name == 'ed'),在真正使用時都得改爲session.query(User).filter(User.name == 'ed')形式,否則只後看到報錯「NameError: name 'query' is not defined」。

 

2.7.2 參數傳遞問題

咱們上邊的sql直接是our_user = session.query(User).filter_by(name='ed').first()形式,但到實際中時User部分和name=‘ed’這部分是經過參數傳過來的,使用參數傳遞時就要注意如下兩個問題。

首先,是參數不要使用引號括起來。好比以下形式是錯誤的(使用引號),將報錯sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such column

table_and_column_name = "User"
filter = "name='ed'"

our_user = session.query(table_and_column_name).filter_by(filter).first()

其次,對於有等號參數須要變換形式。以下去掉了引號,對table_and_column_name沒問題,但filter = (name='ed')這種寫法在python是不容許的

table_and_column_name = User
# 下面這條語句不符合語法
filter = (name='ed')

our_user = session.query(table_and_column_name).filter_by(filter).first()

對參數中帶等號的這種形式,如今能想到的只有使用filter代替filter_by,即將sql語句中的=號轉變爲python語句中的==。正確寫法以下:

table_and_column_name = User
filter = (User.name=='ed')

our_user = session.query(table_and_column_name).filter(filter).first()

 

 

2.8 改(修改users表中的記錄)

# 要修改須要先將記錄查出來
mod_user = session.query(User).filter_by(name='ed').first()

# 將ed用戶的密碼修改成modify_paswd
mod_user.password = 'modify_passwd'

# 確認修改
session.commit()

 

2.9 刪(刪除users表中的記錄)

# 要刪除須要先將記錄查出來
del_user = session.query(User).filter_by(name='ed').first()

# 打印一下,確認未刪除前記錄存在
del_user

# 將ed用戶記錄刪除
session.delete(del_user)

# 確認刪除
session.commit()

# 遍歷查看,已無ed用戶記錄
for user in session.query(User):
    print(user)

 

2.10 直接執行SQL語句

雖然使用框架規定形式能夠在必定程度上解決各數據庫的SQL差別,好比獲取前兩條記錄各數據庫形式以下。

# mssql/access
select top 2 * from table_name;

# mysql
select * from table_name limit 2;

# oracle
select * from table_name where rownum <= 2;

但框架存消除各數據庫SQL差別的同時會引入各框架CRUD的差別,而開發人員每每就有必定的SQL基礎,若是一個框架強制用戶只能使用其規定的CRUD形式那反而增長用戶的學習成本,這個框架註定不能成爲成功的框架。直接地執行SQL而不是使用框架設定的CRUD不該當是一種低級的操做應當是一種被鼓厲的標準化行爲。

# 正常的SQL語句
sql = "select * from users"

# sqlalchemy使用execute方法直接執行SQL
records = session.execute(sql)

 

參考:

https://docs.sqlalchemy.org/en/latest/orm/tutorial.html

https://stackoverflow.com/questions/34009296/using-sqlalchemy-session-from-flask-raises-sqlite-objects-created-in-a-thread-c

相關文章
相關標籤/搜索