sqlalchemy的基本操作大全
SQLAlchemy
是一個數據庫的ORM框架,安裝命令爲pip install sqlalchemy
ORM是什麼
ORM:Object Relationship Mapping,既對象關係映射,通過ORM我們可以通過類的方式去操作數據庫,而不用再寫原生的SQL語句。
通過把表映射成類,把行作爲實例對象,把字段作爲類屬性,ORM在執行對象操作的時候最終還是會把對應的操作轉化爲數據庫原生語句。
其主要優點有:
- 易用
- 性能損耗小
- 設計靈活
- 可移植性強
連接數據庫
from sqlalchemy import create_engine
# 配置鏈接數據庫信息
db_config = {
'host': '127.0.0.1',
'port': '3306',
'database': 'flaskdemo',
'username': 'root',
'password': 'passwd'
}
# 數據庫鏈接地址
db_url = 'mysql+pymysql://{username}:{password}@{host}:{port}/{database}?charset=utf8'.format(**db_config)
# 創建數據庫引擎
engine = create_engine(db_url)
# 創建數據庫鏈接
with engine.connect() as conn:
# 測試是否鏈接成功
result = conn.execute('select 1')
print(result.fetchone())
其中,數據庫連接的地址格式爲
dialect+driver://username:password@host:port/database
- dialect:數據庫類型,比如mysql,sqlite,注意:一定要是小寫
- driver:是python對應的驅動,如果不指定,則會使用默認的驅動,比如MySQL默認驅動是MySQLdb
- username:數據庫用戶名
- password:數據庫密碼
- host:數據庫的域名
- port:數據庫監聽的端口,通常默認是3306
- database:連接的數據庫的名字
連接地址後面還可以加上查詢字符串來設定,如charset=utf8
將ORM模型映射到數據庫中:
-
用
declarative_base
根據engine
創建一個ORM基類。from sqlalchemy.ext.declarative import declarative_base engine = create_engine(DB_URI) Base = declarative_base(engine)
-
用這個
Base
類作爲基類來寫自己的ORM類。要定義__tablename__
類屬性,來指定這個模型映射到數據庫中的表名。class Person(Base): __tablename__ = 'person'
-
創建屬性來映射到表中的字段,所有需要映射到表中的屬性都應該爲Column類型:
class Person(Base): __tablename__ = 'person' # 2. 在這個ORM模型中創建一些屬性,來跟表中的字段進行一一映射。這些屬性必須是sqlalchemy給我們提供好的數據類型。 id = Column(Integer,primary_key=True,autoincrement=True) name = Column(String(50)) age = Column(Integer)
-
使用
Base.metadata.create_all()
來將模型映射到數據庫中。 -
一旦使用
Base.metadata.create_all()
將模型映射到數據庫中後,即使改變了模型的字段,也不會重新映射了。
例如
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
# 配置鏈接數據庫信息
db_config = {
'host': '127.0.0.1',
'port': '3306',
'database': 'flaskdemo',
'username': 'root',
'password': 'passwd'
}
# 數據庫鏈接地址
db_url = 'mysql+pymysql://{username}:{password}@{host}:{port}/{database}?charset=utf8'.format(**db_config)
# 創建數據庫引擎
engine = create_engine(db_url)
# 創建一個基類來繼承
Base = declarative_base(engine)
# 1. 創建一個ORM模型,這個ORM模型必須繼承自sqlalchemy給我們提供好的基類
class Person(Base):
# 表名
__tablename__ = 'person'
# 2. 在這個ORM模型中創建一些屬性,來跟表中的字段進行一一映射,這些屬性必須是sqlalchemy提供好的數據類型
# 設定id爲Int,主鍵, 自增長
id = Column(Integer, primary_key=True, autoincrement=True)
# String類型需要指定長度
name = Column(String(50))
age = Column(Integer)
# 3.將創建好的ORM模型映射到數據庫中
Base.metadata.create_all()
增刪改查
在sqlalchemy中,增刪改查都是通過**會話(seesion)**進行的,所以我們必須要先創建會話,核心代碼如下
from sqlalchemy.orm import sessionmaker
engine = create_engine(db_url)
# 創建一個會話
session = sessionmaker(engine)()
增
-
創建對象,也即創建一條數據:
p1 = Person(name='張三', age=18)
-
將這個對象添加到
session
會話對象中:session.add(p1)
-
將session中的對象做commit操作(提交),注意:增刪改必須提交才能生效:
session.commit()
-
一次性添加多條數據:
p2 = Person(name='李四', age=19) p3 = Person(name='王五', age=20) session.add_all([p1,p2]) session.commit()
刪
-
直接刪除已提交的數據
session.delete(p1) session.commit()
-
刪除從數據庫查找出來的數據
person = session.query(Person).first() session.delete(person) session.commit()
改
和刪除類似,直接對對象進行操作後,提交即可
# 因爲p2已經在會話中了,可以直接更改後再次提交
p2.name = '食鐵獸'
session.commit()
查
先用query
指定查找的類,也就是查找的其對應數據表,獲得該表中所有的數據,並生成一個查找對象
query_person = session.query(Person)
接下來我們就可以通過一些條件,來進行查找
-
all:返回查找對象的所有的數據,組成一個列表
all_person = query_person.all() for person in all_person: print(person)
-
first:返回查找對象的第一條數據
person = query_person.first() print(person)
-
get:找到表中主鍵與參數相同的數據,返回一條數據或None
person = query_person.get(2) print(person)
-
filter_by:通過參數中的值,來查找符合參數中的值的數據,返回一個查找對象
all_person = query_person.filter_by(name='食鐵獸').all() for person in all_person: print(person)
-
filter:通過參數中的布爾表達式,來查找使布爾表達式爲True的數據,返回一個查找對象,注意:參數必須是類.屬性名,功能比filter_by更強
person = query_person.filter(Person.name == '王五').first() print(person)
有關query和filter的還有很多其他的功能,後續介紹
字段常用數據類型
-
Integer:整形,映射到數據庫中是int類型。
-
Float:浮點類型,映射到數據庫中是float類型。他佔據的32位。
-
Double:雙精度浮點類型,映射到數據庫中是double類型,佔據64位。
-
String:可變字符類型,映射到數據庫中是varchar類型.
-
Boolean:布爾類型,映射到數據庫中的是tinyint類型。
-
DECIMAL:定點類型。是專門爲了解決浮點類型精度丟失的問題的。在存儲錢相關的字段的時候建議大家都使用這個數據類型。並且這個類型使用的時候需要傳遞兩個參數,第一個參數是用來標記這個字段總能能存儲多少個數字,第二個參數表示小數點後有多少位。
-
Enum:枚舉類型。指定某個字段只能是枚舉中指定的幾個值,不能爲其他值。在ORM模型中,使用Enum來作爲枚舉,示例代碼如下:
class Article(Base): __tablename__ = 'article' id = Column(Integer,primary_key=True,autoincrement=True) tag = Column(Enum("python",'flask','django'))
-
在Python3中,已經內置了enum這個枚舉的模塊,我們也可以使用這個模塊去定義相關的字段。示例代碼如下:
class TagEnum(enum.Enum): python = "python" flask = "flask" django = "django" class Article(Base): __tablename__ = 'article' id = Column(Integer,primary_key=True,autoincrement=True) tag = Column(Enum(TagEnum)) article = Article(tag=TagEnum.flask)
-
Date:存儲時間,只能存儲年月日。映射到數據庫中是date類型。在Python代碼中,可以使用
datetime.date
來指定。示例代碼如下:class Article(Base): __tablename__ = 'article' id = Column(Integer,primary_key=True,autoincrement=True) create_time = Column(Date) article = Article(create_time=date(2017,10,10))
-
DateTime:存儲時間,可以存儲年月日時分秒毫秒等。映射到數據庫中也是datetime類型。在Python代碼中,可以使用
datetime.datetime
來指定。示例代碼如下:class Article(Base): __tablename__ = 'article' id = Column(Integer,primary_key=True,autoincrement=True) create_time = Column(DateTime) article = Article(create_time=datetime(2011,11,11,11,11,11))
-
Time:存儲時間,可以存儲時分秒。映射到數據庫中也是time類型。在Python代碼中,可以使用
datetime.time
來至此那個。示例代碼如下:class Article(Base): __tablename__ = 'article' id = Column(Integer,primary_key=True,autoincrement=True) create_time = Column(Time) article = Article(create_time=time(hour=11,minute=11,second=11))
-
Text:存儲長字符串。一般可以存儲6W多個字符。如果超出了這個範圍,可以使用LONGTEXT類型。映射到數據庫中就是text類型。
-
LONGTEXT:長文本類型,映射到數據庫中是longtext類型。
字段常用的屬性設置
- primary_key:設置某個字段爲主鍵。
- autoincrement:設置這個字段爲自動增長的。
- default:設置某個字段的默認值。在發表時間這些字段上面經常用。
- nullable:指定某個字段是否爲空。默認值是True,就是可以爲空。
- unique:指定某個字段的值是否唯一。默認是False。
- onupdate:在數據更新的時候會調用這個參數指定的值或者函數。在第一次插入這條數據的時候,不會用onupdate的值,只會使用default的值。常用的就是
update_time
(每次更新數據的時候都要更新的值)。 - name:指定ORM模型中某個屬性映射到表中的字段名。如果不指定,那麼會使用這個屬性的名字來作爲字段名。如果指定了,就會使用指定的這個值作爲參數。這個參數也可以當作位置參數,在第1個參數來指定。
- comment:設置該字段的註釋
query可用參數與聚合函數
-
模型對象:指定查找這個模型中所有的對象。
-
模型中的屬性:可以指定只查找某個模型的其中幾個屬性,會將數據的這幾個屬性打包成元組
p1 = Person(name='張三', age=18) p2 = Person(name='李四', age=19) p3 = Person(name='王五', age=21) session.add_all([p1, p2, p3]) session.commit() print(session.query(Person.name, Person.age).all())
-
結果爲
[(‘張三’, 18), (‘李四’, 19), (‘王五’, 21)]
-
聚合函數:需要從
sqlalchemy
導入func
- func.count:統計行的數量。注意:結果存放在一個元組中,下同
- func.avg:求平均值。
- func.max:求最大值。
- func.min:求最小值。
- func.sum:求和。
func
上,其實沒有任何聚合函數。但是因爲他底層做了一些魔術,只要mysql中有的聚合函數,都可以通過func調用
print(session.query(func.count(Person.id)).first()) print(session.query(func.avg(Person.age)).first()) print(session.query(func.max(Person.age)).first())
-
結果爲
(3,)
(Decimal(‘19.3333’),)
(21,)
filter過濾條件
過濾是數據提取的一個很重要的功能,以下對一些常用的過濾條件進行解釋,並且這些過濾條件都是隻能通過filter方法實現的:
-
equals:
article = session.query(Article).filter(Article.title == "title0").first() print(article)
-
not equals:
query.filter(User.name != 'ed')
-
like:
query.filter(User.name.like('%ed%'))
-
in:
query.filter(User.name.in_(['ed','wendy','jack'])) # 同時,in也可以作用於一個Query query.filter(User.name.in_(session.query(User.name).filter(User.name.like('%ed%'))))
-
not in:
query.filter(~User.name.in_(['ed','wendy','jack'])) # 或者是 query.filter(User.name.notin_(['ed','wendy','jack']))
-
is null:
query.filter(User.name==None) # 或者是 query.filter(User.name.is_(None))
-
is not null:
query.filter(User.name != None) # 或者是 query.filter(User.name.isnot(None))
-
and:
from sqlalchemy import and_ query.filter(and_(User.name=='ed',User.fullname=='Ed Jones')) # 或者是傳遞多個參數 query.filter(User.name=='ed',User.fullname=='Ed Jones') # 或者是通過多次filter操作 query.filter(User.name=='ed').filter(User.fullname=='Ed Jones')
-
or:
from sqlalchemy import or_ query.filter(or_(User.name=='ed',User.name=='wendy'))
如果想要查看orm底層轉換的sql語句,可以在filter方法後面不要再執行任何方法直接打印就可以看到了。比如:
articles = session.query(Article).filter(or_(Article.title=='abc',Article.content=='abc'))
print(articles)
外鍵和約束
使用SQLAlchemy創建外鍵非常簡單。在從表中增加一個字段,指定這個字段外鍵的是哪個表的哪個字段就可以了。從表中外鍵的字段,必須和父表的主鍵字段類型保持一致。
示例代碼如下:
from sqlalchemy import create_engine, Column, Integer, String, func, ForeignKey
class User(Base):
__tablename__ = 'user'
id = Column(Integer,primary_key=True,autoincrement=True)
username = Column(String(50),nullable=False)
class Article(Base):
__tablename__ = 'article'
id = Column(Integer,primary_key=True,autoincrement=True)
title = Column(String(50),nullable=False)
content = Column(Text,nullable=False)
uid = Column(Integer,ForeignKey("user.id"), ondelete="SET NULL")
外鍵約束,寫在Colum中的關鍵字參數ondelete
中,有以下幾項:
RESTRICT
:父表數據被刪除,會阻止刪除。默認就是這一項。NO ACTION
:在MySQL中,同RESTRICT。CASCADE
:級聯刪除。SET NULL
:父表數據被刪除,子表數據會設置爲NULL。
注意:這裏設定的約束,僅對於從sql刪除有效,若從ORM層面來刪除數據,將會無視外鍵約束,全部視爲set null
注意:ForeignKey裏的字符串格式不是類名.屬性名,而是表名.字段名
ORM關係以及一對多
mysql級別的外鍵,還不夠ORM,必須拿到一個表的外鍵,然後通過這個外鍵再去另外一張表中查找,這樣太麻煩了。SQLAlchemy提供了一個relationship
,這個類可以定義屬性,以後在訪問相關聯的表的時候就直接可以通過屬性訪問的方式就可以訪問得到了。示例代碼:
導入ralationship:
from sqlalchemy.orm import relationship
例如:
class Author(Base):
__tablename__ = 'author'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(10), nullable=False)
# 關聯,默認爲一對多,有外鍵的是多,被引的是一,這個地方寫的是類名
books = relationship('Book', backref='author')
def __repr__(self):
return '<Author:(id={}, name={})>'.format(self.id, self.name)
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(20), nullable=False)
# 外鍵,表名.字段名
author_id = Column(Integer, ForeignKey('author.id'))
# 關聯,默認爲一對多,有外鍵的是多,被引的是一,這個地方寫的是類名
# author = relationship("Author", backref="books")
def __repr__(self):
return '<Book:(id={}, name={}, author_id={})>'.format(self.id, self.name, self.author_id)
# 插入數據
Base.metadata.drop_all()
Base.metadata.create_all()
author1 = Author(name='張三')
author2 = Author(name='李四')
book1 = Book(name='python從入門到入墳', author_id=1)
book2 = Book(name='如何讓富婆喜歡你', author_id=1)
session.add_all([author1, author2, book1, book2])
session.commit()
# 查找
book = session.query(Book).get(1)
print(book.author)
author = session.query(Author).get(1)
print(author.books)
結果爲:
<Author:(id=1, name=張三)>
[<Book:(id=1, name=python從入門到入墳, author_id=1)>, <Book:(id=2, name=如何讓富婆喜歡你, author_id=1)>]
另外,可以通過backref
來指定反向訪問的屬性名稱。articles是有多個。默認他們之間的關係是一個一對多的關係,有外鍵的是多,被引的是一。
relationship
寫在哪一方都可以
對於Book,每個對象都關聯一個Author對象,所以其關聯類型就是其Author
而對於Author,因爲其關聯了多個對象,所以關聯類型是列表
注意:relationship
的第一個參數寫的不是表名,而是關聯的ORM類名
關聯插入
因爲我們ORM處理的都是類和對象,所以對於有外鍵的類,我們都不喜歡直接給外鍵賦值,而希望可以用對象關聯來自動給外鍵賦值,sqlalchemy確實有實現這樣的功能,向數據庫增加一條數據後,其關聯屬性的對象也會跟着加入,例如
# 插入數據
author1 = Author(name='張三')
author2 = Author(name='李四')
book1 = Book(name='python從入門到入墳')
book2 = Book(name='如何讓富婆喜歡你')
book3 = Book(name='朝花夕拾')
# 關聯插入
author1.books.append(book1)#列表添加
author1.books.append(book2)
book3.author = author2#直接賦值
session.add(author1)
session.add(book3)
session.commit()
# 查找
book = session.query(Book).get(3)
print(book.author)
author = session.query(Author).get(1)
print(author.books)
結果爲:
<Author:(id=2, name=李四)>
[<Book:(id=1, name=python從入門到入墳, author_id=1)>, <Book:(id=2, name=如何讓富婆喜歡你, author_id=1)>]
因爲author關聯多個對象,所以其books的類型是列表,可以使用append方法添加關聯對象
而book關聯一個對象,所以其類型就是Author,直接賦值即可
一對一的關係:
在sqlalchemy中,如果想要將兩個模型映射成一對一的關係,那麼應該在父模型中,指定引用的時候,要傳遞一個uselist=False
這個參數進去。就是告訴父模型,以後引用這個從模型的時候,不再是一個列表了,而是一個對象了。示例代碼如下:
class User(Base):
__tablename__ = 'user'
id = Column(Integer,primary_key=True,autoincrement=True)
username = Column(String(50),nullable=False)
extend = relationship("UserExtend",uselist=False)
def __repr__(self):
return "<User(username:%s)>" % self.username
class UserExtend(Base):
__tablename__ = 'user_extend'
id = Column(Integer, primary_key=True, autoincrement=True)
school = Column(String(50))
uid = Column(Integer,ForeignKey("user.id"))
user = relationship("User",backref="extend")
當然,也可以藉助sqlalchemy.orm.backref
來簡化代碼:
class User(Base):
__tablename__ = 'user'
id = Column(Integer,primary_key=True,autoincrement=True)
username = Column(String(50),nullable=False)
# extend = relationship("UserExtend",uselist=False)
def __repr__(self):
return "<User(username:%s)>" % self.username
class UserExtend(Base):
__tablename__ = 'user_extend'
id = Column(Integer, primary_key=True, autoincrement=True)
school = Column(String(50))
uid = Column(Integer,ForeignKey("user.id"))
user = relationship("User",backref=backref("extend",uselist=False))
這時,要想使用關聯插入,兩邊的關聯屬性都直接賦值即可
多對多的關係:
根據數據庫的只是,多對多的關係,應該採用加入一張中間表的方式來解決,中間表使用Table
類來創建
導入Table類
from sqlalchemy import Table
- 1
- 多對多的關係需要通過一張中間表來綁定他們之間的關係。
- 先把兩個需要做多對多的模型定義出來
- 使用Table定義一箇中間表,中間表一般就是包含兩個模型的外鍵字段就可以了,並且讓他們兩個來作爲一個“複合主鍵”。
- 在兩個需要做多對多的模型中隨便選擇一個模型,定義一個relationship屬性,來綁定三者之間的關係,在使用relationship的時候,需要傳入一個secondary=中間表。
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(20), nullable=False)
# 外鍵,表名.字段名
author_id = Column(Integer, ForeignKey('author.id'))
# 關聯,默認爲一對多,有外鍵的是多,被引的是一,這個地方寫的是類名
# author = relationship("Author", backref="books")
# 多對多關聯,secondary指定中間表,這樣的話,book中有tags,tag中有books,其關聯信息的外鍵將放到book_tag中
tags = relationship('Tag', backref='books', secondary='book_tag')
def __repr__(self):
return '<Book:(id={}, name={}, author_id={})>'.format(self.id, self.name, self.author_id)
class Tag(Base):
__tablename__ = 'tag'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(10), nullable=False)
# 多對多中間表,第一個參數時表名,第二個參數傳Base.metadata
book_tag = Table(
'book_tag',
Base.metadata,
# 組合成複合主鍵
Column('book_id', Integer, ForeignKey('book.id'), primary_key=True),
Column('tag_id', Integer, ForeignKey('tag.id'), primary_key=True)
)
book1 = Book(name='python從入門到入墳')
book2 = Book(name='如何讓富婆喜歡你')
tag1 = Tag(name='推理')
tag2 = Tag(name='言情')
book1.tags.append(tag1)
book1.tags.append(tag2)
book2.tags.append(tag1)
book2.tags.append(tag2)
session.add_all([book1, book2])
session.commit()
ORM層面的刪除數據:
ORM層面刪除數據,會無視mysql級別的外鍵約束。先將從表中的那個外鍵設置爲NULL,再將對應的數據刪除,。如果想要避免這種行爲,應該將從表中的外鍵的nullable=False
。
在SQLAlchemy,只要將一個數據添加到session中,和他相關聯的數據都可以一起存入到數據庫中了。這些是怎麼設置的呢?其實是通過relationship的時候,有一個關鍵字參數cascade可以設置這些屬性,多個屬性使用英文逗號,
隔開:
- save-update:默認選項。在添加一條數據的時候,會把其他和他相關聯的數據都添加到數據庫中。這種行爲就是save-update屬性影響的。
- delete:表示當刪除某一個模型中的數據的時候,是否也刪掉使用relationship和他關聯的數據。
- delete-orphan:表示當對一個ORM對象解除了父表中的關聯對象的時候,自己便會被刪除掉。當然如果父表中的數據被刪除,自己也會被刪除。這個選項只能用在一對多上,不能用在多對多以及多對一上。並且還需要在子模型中的relationship中,增加一個single_parent=True的參數。
- merge:默認選項。當在使用session.merge,合併一個對象的時候,會將使用了relationship相關聯的對象也進行merge操作。
- expunge:移除操作的時候,會將相關聯的對象也進行移除。這個操作只是從session中移除,並不會真正的從數據庫中刪除。
- all:是對save-update, merge, refresh-expire, expunge, delete幾種的縮寫。
排序
-
order_by:調用查詢對象的order_by方法,可以指定根據這個表中的某個字段進行排序
其參數可以是
類.屬性
,也可以是屬性名字符串,推薦第一種寫法對於
類.屬性
形式,如果在前面加了一個-,代表的是降序排序。例如:
class Book(Base): __tablename__ = 'book' id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(20), nullable=False) # 外鍵,表名.字段名 author_id = Column(Integer, ForeignKey('author.id')) # 注意,datetime.now後不要加括號,加了之後,它就變成一個常數了,所有的Book默認值都是一個常數 # 而不加括號,則會在每個對象添加進數據庫的時候,自動調用datetime.now方法 release_time = Column(DateTime, default=datetime.now) def __repr__(self): return '<Book:(name={}, release_time={})>'.format(self.name, self.release_time) # 類屬性形式 # 升序 all_book = session.query(Book).order_by(Book.release_time).all() # 降序 all_book = session.query(Book).order_by(-Book.release_time).all() all_book = session.query(Book).order_by(Book.release_time.desc()).all() # 字符串形式 # 升序 all_book = session.query(Book).order_by('release_time').all()
-
在模型定義的時候指定默認排序:有些時候,不想每次在查詢的時候都指定排序的方式,可以在定義模型的時候就指定排序的方式。有以下兩種方式:
-
relationship的order_by參數:在指定relationship的時候,傳遞order_by參數來指定排序的字段。
當通過關聯獲取對象時,會默認排序,降序需要用desc方法,例如:
author = relationship("Author", backref=backref('books', order_by=release_time.desc()))
-
這樣當通過Author對象獲取其所有Book對象時,就會讓Book對象降序排序
-
在模型定義中,添加以下代碼:
__mapper_args__ = { "order_by": title }
-
即可讓文章使用標題來進行排序。
-
-
正序排序與倒序排序:默認是使用正序排序。如果需要使用倒序排序,那麼可以使用這個字段的
desc()
方法,或者是在排序的時候使用這個字段的字符串名字,然後在前面加一個負號。
limit、offset和切片操作:
-
limit:可以限制每次查詢的時候只查詢幾條數據。
-
offset:可以限制查找數據的時候過濾掉前面多少條。
# 選出從第11本書開始(包括)後面10本書,即第11至20本書 part_book = session.query(Book).offset(10).limit(10).all()
-
切片:可以對Query對象使用切片操作,來獲取想要的數據。可以使用
slice(start,stop)
方法來做切片操作。也可以使用[start:stop]
的方式來進行切片操作。一般在實際開發中,中括號的形式是用得比較多的。希望大家一定要掌握。示例代碼如下:# 拿到最後10本書 books = session.query(Book).order_by(Book.id.desc())[0:10]
懶加載
在一對多,或者多對多的時候,如果想要獲取多的這一部分的數據的時候,往往能通過一個屬性就可以全部獲取了。比如有一個作者,想要或者這個作者的所有文章,那麼可以通過user.articles就可以獲取所有的。但有時候我們不想獲取所有的數據,比如只想獲取這個作者今天發表的文章,那麼這時候我們可以給relationship傳遞一個lazy=‘dynamic’,以後通過user.articles獲取到的就不是一個列表,而是一個AppenderQuery對象了。這樣就可以對這個對象再進行一層過濾和排序等操作。
通過lazy='dynamic'
,獲取出來的多的那一部分的數據,就是一個AppenderQuery
對象了。這種對象既可以添加新數據,使用append方法,也可以跟Query
一樣,可以再進行一層過濾。
總而言之一句話:如果你在獲取數據的時候,想要對多的那一邊的數據再進行一層過濾,那麼這時候就可以考慮使用lazy='dynamic'
。
lazy可用的選項:
select
:這個是默認選項。還是拿user.articles
的例子來講。如果你沒有訪問user.articles
這個屬性,那麼sqlalchemy就不會從數據庫中查找文章。一旦你訪問了這個屬性,那麼sqlalchemy就會立馬從數據庫中查找所有的文章,並把查找出來的數據組裝成一個列表返回。這也是懶加載。dynamic
:這個就是我們剛剛講的。就是在訪問user.articles
的時候返回回來的不是一個列表,而是AppenderQuery
對象。
author = db.relationship('Author', backref='books', lazy='dynamic')
group_by:
根據某個字段進行分組。比如想要根據性別進行分組,來統計每個分組分別有多少人,那麼可以使用以下代碼來完成:
session.query(User.gender,func.count(User.id)).group_by(User.gender).all()
分組常常配合聚合函數使用
having:
having是對查找結果進一步過濾。比如只想要看未成年人的數量,那麼可以首先對年齡進行分組統計人數,然後再對分組進行having過濾。示例代碼如下:
result = session.query(User.age,func.count(User.id)).group_by(User.age).having(User.age >= 18).all()
join:
- join分爲left join(左外連接)和right join(右外連接)以及內連接(等值連接)。
- 參考的網頁:http://www.jb51.net/article/15386.htm
- 在sqlalchemy中,使用join來完成內連接。在寫join的時候,如果不寫join的條件,那麼默認將使用外鍵來作爲條件連接。
- query查找出來什麼值,不會取決於join後面的東西,而是取決於query方法中傳了什麼參數。就跟原生sql中的select 後面那一個一樣。
比如現在要實現一個功能,要查找所有用戶,按照發表文章的數量來進行排序。示例代碼如下:
result = session.query(User,func.count(Article.id)).join(Article).group_by(User.id).order_by(func.count(Article.id).desc()).all()
subquery:
子查詢可以讓多個查詢變成一個查詢,只要查找一次數據庫,性能相對來講更加高效一點。不用寫多個sql語句就可以實現一些複雜的查詢。那麼在sqlalchemy中,要實現一個子查詢,應該使用以下幾個步驟:
- 將子查詢按照傳統的方式寫好查詢代碼,然後在
query
對象後面執行subquery
方法,將這個查詢變成一個子查詢。 - 在子查詢中,將以後需要用到的字段通過
label
方法,取個別名。 - 在父查詢中,如果想要使用子查詢的字段,那麼可以通過子查詢的返回值上的
c
屬性拿到。
整體的示例代碼如下:
stmt = session.query(User.city.label("city"),User.age.label("age")).filter(User.username=='李A').subquery()
result = session.query(User).filter(User.city==stmt.c.city,User.age==stmt.c.age).all()