1. 程式人生 > >python ORM 框架 sqlalchemy

python ORM 框架 sqlalchemy

sqlalchemy 是一款Python語言寫的ORM框架, 該框架建立在資料庫API基礎之上。

 

sqlalchemy 本身無法操作資料庫,必須已第三方外掛為基礎,Dialect用於和資料API進行交流,根據不通的的配置呼叫不通的資料庫API,從而實現對資料庫的操作。

 

1

2

3

4

5

6

7

8

9

10

11

MySQL-Python

    mysql+mysqldb://<user>:<password>@<host>[:<port>]

/<dbname>

   

pymysql

    mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]

   

MySQL-Connector

    mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>

   

cx_Oracle

    oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]

 

ORM 

一、建立表

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

from

 sqlalchemy.ext.declarative import declarative_base

from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index

from sqlalchemy.orm import sessionmaker, relationship

from sqlalchemy import create_engine

 

engine = create_engine("mysql+pymysql://root:[email protected]:3306/mybase", max_overflow=5)

 

BaseModel = declarative_base()

 

class Blog(BaseModel):

    __tablename__ = 'blog'

 

    id = Column(CHAR(32), primary_key=True)

    title = Column(String(64), server_default='', nullable=False)

    text = Column(String, server_default='', nullable=False)

    user = Column(CHAR(32), index=True, server_default='', nullable=False)

    create = Column(BIGINT, index=True, server_default='0', nullable=False)

 

 

class User(BaseModel):

    __tablename__ = 'user'

 

    id = Column(CHAR(32), primary_key=True)

    name = Column(String(32), server_default='', nullable=False)

    username = Column(String(32), index=True, server_default='', nullable=False)

    password = Column(String(64), server_default='', nullable=False)

 

 

def init_db():

    BaseModel.metadata.create_all(Engine) # 建立資料庫及表

 

def drop_db():

    BaseModel.metadata.drop_all(Engine) # 刪除資料庫及表

 

 

if __name__ == '__main__':

    #init_db()

    drop_db()

    #BaseModel.metadata.tables['user'].create(Engine, checkfirst=True)

    #BaseModel.metadata.tables['user'].drop(Engine, checkfirst=False)

    pass

  

二、向資料庫新增資料

1

2

3

4

5

6

7

8

session = Session()

session.add(User(id=uuid.uuid4().hex))

session.add(Blog(id=uuid.uuid4().hex))

session.add_all([

    User(id=uuid.uuid4().hex),

    Blog(id=uuid.uuid4().hex)

])

session.commit()

 

三、查詢

查詢的結果, 有幾種不同的型別, 這個需要注意, 像是:

  • instance
  • instance of list
  • keyed tuple of list
  • value of list

1、普通查詢

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

session.query(User).filter_by(username='abc').all()

session.query(User).filter(User.username=='abc').all()

session.query(Blog).filter(Blog.create >= 0).all()

session.query(Blog).filter(Blog.create >= 0).first()

session.query(Blog).filter(Blog.create >= 0 | Blog.title == 'A').first()

session.query(Blog).filter(Blog.create >= 0 & Blog.title == 'A').first()

session.query(Blog).filter(Blog.create >= 0).offset(1).limit(1).scalar()

session.query(User).filter(User.username ==  'abc').scalar()

session.query(User.id).filter(User.username ==  'abc').scalar()

session.query(Blog.id).filter(Blog.create >= 0).all()

session.query(Blog.id).filter(Blog.create >= 0).all()[0].id

dict(session.query(Blog.id, Blog.title).filter(Blog.create >= 0).all())

session.query(Blog.id, Blog.title).filter(Blog.create >= 0).first().title

session.query(User.id).order_by('id desc').all()

session.query(User.id).order_by('id').first()

session.query(User.id).order_by(User.id).first()

session.query(User.id).order_by(-User.id).first()

session.query('id''username').select_from(User).all()

session.query(User).get('16e19a64d5874c308421e1a835b01c69')

  

 2、多表查詢

1

2

3

4

5

session.query(Blog, User).filter(Blog.user == User.id).first().User.username

session.query(Blog, User.id, User.username).filter(Blog.user == User.id).first().id

session.query(Blog.id,

              User.id,

              User.username).filter(Blog.user == User.id).first().keys()

  

3、條件查詢

1

2

3

4

5

6

7

8

9

from sqlalchemy import or_, not_

 

session.query(User).filter(or_(User.id == '',

                               User.id == '16e19a64d5874c308421e1a835b01c69')).all()

session.query(User).filter(not_(User.id == '16e19a64d5874c308421e1a835b01c69')).all()

session.query(User).filter(User.id.in_(['16e19a64d5874c308421e1a835b01c69'])).all()

session.query(User).filter(User.id.like('16e19a%')).all()

session.query(User).filter(User.id.startswith('16e19a')).all()

dir(User.id)

  

4、函式

1

2

3

4

5

6

7

from sqlalchemy import func

session.query(func.count('1')).select_from(User).scalar()

session.query(func.count('1'), func.max(User.username)).select_from(User).first()

session.query(func.count('1')).select_from(User).scalar()

session.query(func.md5(User.username)).select_from(User).all()

session.query(func.current_timestamp()).scalar()

session.query(User).count()

 

四、修改

兩種修改方式

1

2

3

4

5

6

session.query(User).filter(User.username == 'abc').update({'name''123'})

session.commit()

 

user = session.query(User).filter_by(username='abc').scalar()

user.name = '111'

session.commit()

 

如果涉及對屬性原值的引用, 則要考慮 synchronize_session 這個引數.

  • 'evaluate' 預設值, 會同時修改當前 session 中的物件屬性.
  • 'fetch' 修改前, 會先通過 select 查詢條目的值.
  • ‘False’ 不修改當前 session 中的物件屬性.

在預設情況下, 因為會有修改當前會話中的物件屬性, 所以如果語句中有 SQL 函式, 或者"原值引用", 那是無法完成的操作, 自然也會報錯, 比如:

1

2

3

from sqlalchemy import func

session.query(User).update({User.name: func.trim('123 ')}) # 使用了函式

session.query(User).update({User.name: User.name + 'x'}) #使用了原值引用<br><br># 以上兩種情況都會報錯

 

這種情況下, 就不能要求 SQLAlchemy 修改當前 session 的物件屬性了, 而是直接進行資料庫的互動, 不管當前會話值(將synchronize_session值設定為False):

1

session.query(User).update({User.name: User.name + 'x'}, synchronize_session=False)

  

是否修改當前會話的物件屬性, 涉及到當前會話的狀態. 如果當前會話過期, 那麼在獲取相關物件的屬性值時, SQLAlchemy 會自動作一次資料庫查詢, 以便獲取正確的值:

1

2

3

4

5

6

user = session.query(User).filter_by(username='abc').scalar()

print user.name

session.query(User).update({User.name: 'new'}, synchronize_session=fetch)

print user.name

session.commit()

print user.name

 

執行了 update 之後, 雖然相關物件的實際的屬性值已變更, 但是當前會話中的物件屬性值並沒有改變. 直到 session.commit() 之後, 當前會話變成"過期"狀態, 再次獲取 user.name 時, SQLAlchemy 通過 user 的 id 屬性, 重新去資料庫查詢了新值. 

synchronize_session 設定成 'fetch' 不會有這樣的問題, 因為在做 update 時已經修改了當前會話中的物件了.

不管 synchronize_session 的行為如何, commit 之後 session 都會過期, 再次獲取相關物件值時, 都會重新作一次查詢.

 

好好體會上邊的話。

 

五、刪除

刪除同樣有像修改一樣的 synchronize_session 引數的問題, 影響當前會話的狀態.

1

2

3

4

session.query(User).filter_by(username='abc').delete()

 

user = session.query(User).filter_by(username='abc').first()

session.delete(user)

 

六、join 操作

sqlalchemy 預設情況下的join 是內連線

1

2

3

= session.query(Blog, User).join(User, Blog.user == User.id).all()

for blog, user in r:

    print blog.id, blog.user, user.id

  

四、外來鍵和關係

1、外來鍵的定義

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

from sqlalchemy import Column, ForeignKey

from sqlalchemy.types import String, Integer, CHAR, BIGINT

 

class Blog(BaseModel):

    __tablename__ = 'blog'

 

    id = Column(BIGINT, primary_key=True, autoincrement=True)

    title = Column(String(64), server_default='', nullable=False)

    text = Column(String, server_default='', nullable=False)

    user = Column(BIGINT, ForeignKey('user.id'), index=True, nullable=False)

    create = Column(BIGINT, index=True, server_default='0', nullable=False)

        user_obj = relationship('User')

 

class User(BaseModel):

    __tablename__ = 'user'

 

    id = Column(BIGINT, primary_key=True, autoincrement=True)

    name = Column(String(32), server_default='', nullable=False)

    username = Column(String(32), index=True, server_default='', nullable=True)

    password = Column(String(64), server_default='', nullable=False)    blog_list = relationship('Blog', order_by='Blog.create')

 

新增資料

1

2

3

4

5

6

7

session = Session()

user = User(name='first', username=u'新的')

session.add(user)

session.flush()

blog = Blog(title=u'第一個', user=user.id)

session.add(blog)

session.commit()

 

session.flush() 是進行資料庫互動, 但是事務並沒有提交. 進行資料庫互動之後, user.id 才有值.

定義了外來鍵, 對查詢來說, 並沒有影響. 外來鍵只是單純的一條約束而已. 當然, 可以在外來鍵上定義一些關聯的事件操作, 比如當外來鍵條目被刪除時, 欄位置成 null , 或者關聯條目也被刪除等

 

獲取資料

1

2

3

session = Session()

print session.query(Blog).get(1).user_obj

print session.query(User).get(1).blog_list

對於 一對多 的關係, 使用 any() 函式查詢:

1

user = session.query(User).filter(User.blogs.any(Blog.title == u'A')).first()

反之, 如果是 多對一 的關係, 則使用 has() 函式查詢:

1

blog = session.query(Blog).filter(Blog.user_obj.has(User.name == u'XX')).first()

 

 

上面的關係定義, 對應的屬性是實際查詢出的例項列表, 當條目數多的時候, 這樣可能會有問題. 比如使用者名稱下有成千上萬的文章, 一次全取出就太暴力了. 關係對應的屬性可以定義成一個 Query 

1

2

3

4

5

6

7

class User(BaseModel):

    __tablename__ = 'user'

 

    id = Column(BIGINT, primary_key=True, autoincrement=True)

    name = Column(String(32), server_default='', nullable=False)

 

    blog_list = relationship('Blog', order_by='Blog.create', lazy="dynamic")

這樣就能自由控制了

1

2

session.query(User).get(1).blog_list.all()

session.query(User).get(1).blog_list.filter(Blog.title == 'abc').first()

 

2、關係的表現形式

關係在物件屬性中的表現, 預設是列表, 但是, 這不是唯一的形式. 根據需要, 可以作成 dictionary , set 或者其它你需要的物件.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

class Blog(BaseModel):

    __tablename__ = 'blog'

 

    id = Column(Integer, autoincrement=True, primary_key=True)

    title = Column(Unicode(32), server_default='')

    user = Column(Integer, ForeignKey('user.id'), index=True)

 

    user_obj = relationship('User')

 

 

class User(BaseModel):

    __tablename__ = 'user'

 

    id = Column(Integer, autoincrement=True, primary_key=True)

    name = Column(Unicode(32), server_default='')

 

    blogs = relationship('Blog')

 

對於上面的兩個模型:

1

2

user = session.query(User).first()

print user.blogs

 

1

2

3

4

5

6

user = User(name=u'XX')

session.add_all([Blog(title=u'A', user_obj=user), Blog(title=u'B', user_obj=user)])

session.commit()

 

user = session.query(User).first()

print user.blogs

  

現在 user.blogs 是一個列表. 我們可以在 relationship() 呼叫時通過 collection_class 引數指定一個類, 來重新定義關係的表現形式:

 

set, 集合

1

2

3

blogs = relationship('Blog', collection_class=set)

 

#InstrumentedSet([<__main__.Blog object at 0x1a58710>, <__main__.Blog object at 0x1a587d0>])

 

attribute_mapped_collection , 字典, 鍵值從屬性取:

1

2

3

4

5

from sqlalchemy.orm.collections import attribute_mapped_collection

 

blogs = relationship('Blog', collection_class=attribute_mapped_collection('title'))

 

#{u'A': <__main__.Blog object at 0x20ed810>, u'B': <__main__.Blog object at 0x20ed8d0>}

 

mapped_collection , 字典, 鍵值自定義:

1

2

3

4

5

from sqlalchemy.orm.collections import mapped_collection

 

blogs = relationship('Blog', collection_class=mapped_collection(lambda blog: blog.title.lower()))

 

#{u'a': <__main__.Blog object at 0x1de4890>, u'b': <__main__.Blog object at 0x1de4950>}

 

出處:https://www.cnblogs.com/9527chu/p/5713191.html