python輕量級orm框架 peewee常用功能速查詳情
一、常見orm數(shù)據(jù)庫框架
Django ORM
peewee
SQLAlchemy
Django ORM
優(yōu)點 :
易用,學習曲線短
和Django
緊密集合,用Django
時使用約定俗成的方法去操作數(shù)據(jù)庫
缺點 :
QuerySet
速度不給力,會逼我用Mysqldb
來操作原生sql語句。
Peewee
優(yōu)點 :
Django
式的API
,使其易用
輕量實現(xiàn),很容易和任意web框架集成
缺點 :
不支持自動化 schema
遷移
不能像Django
那樣,使線上的mysql
表結(jié)構生成結(jié)構化的模型。
SQLAlchemy
優(yōu)點 :
巨牛逼的API,使得代碼有健壯性和適應性
靈活的設計,使得能輕松寫復雜查詢
缺點 :
工作單元概念不常見
重量級 API,導致長學習曲線
1、peewee 簡單demo
import datetime from peewee import * db = MySQLDatabase( "test", host="127.0.0.1", port=3306, user="root", passwd="123456" ) db.connect() class BaseModel(Model): class Meta: database = db class Person(BaseModel): name = CharField() age = IntegerField() height = IntegerField() sex = BooleanField(default='male') if __name__ == "__main__": Person.create_table() # 創(chuàng)建 Person.create(name='tom', age=30, height=177) # 查詢 res = Person.select().where(Person.name=='tom') print(res) print(res[0]) print(res[0].name) print(res[0].age) print(res[0].height) print(res[0].sex) >>>> SELECT `t1`.`id`, `t1`.`name`, `t1`.`age`, `t1`.`High`, `t1`.`sex` FROM `person` AS `t1` WHERE (`t1`.`name` = 'ljk') 1 tom 30 177 True
二、Model 和 Field 關系
在ORM對象關系數(shù)據(jù)庫中 Model是一個類,映射到數(shù)據(jù)庫表中就是一個表。Filed是字段,映射到表中就是字段。model
實例就是數(shù)據(jù)庫中的一條記錄。在peewee中Model
和Field的關系如下:
Thing | 對應關系 |
---|---|
Model 類 | 表 |
Field 實例 | 表中字段 |
Model 實例 | 表中數(shù)據(jù) |
數(shù)據(jù)庫連接和model類定義的 典型使用
import datetime from peewee import * db = SqliteDatabase('my_app.db') class BaseModel(Model): class Meta: database = db class User(BaseModel): username = CharField(unique=True) class Tweet(BaseModel): user = ForeignKeyField(User, backref='tweets') message = TextField() created_date = DateTimeField(default=datetime.datetime.now) is_published = BooleanField(default=True)
創(chuàng)建一個數(shù)據(jù)庫實例
db = SqliteDatabase('my_app.db')
創(chuàng)建一個基礎model類
class BaseModel(Model): class Meta: database = db
定義一個用于建立數(shù)據(jù)庫連接的基模類是一種推薦的做法,因為將不必為后續(xù)表指定數(shù)據(jù)庫。
定義一個普通 model 類
class User(BaseModel): username = CharField(unique=True)
模型定義使用的是其他流行的orm(如SQLAlchemy
或Django
)中看到的聲明式風格。因為User繼承了BaseModel
類,所以User類可以繼承數(shù)據(jù)庫連接。
User已經(jīng)明確定義了一個具有唯一約束的用戶名列。因為我們沒有指定主鍵,peewee 會自動添加一個自增整數(shù)主鍵字段,名為 id。沒有指定主鍵的表peewee會自動創(chuàng)建一個名字為id的自增主鍵。
三、Model 模型
為了不污染model的命名空間,model的配置放在特殊的元屬性類中。這是從Django的框架中借鑒過來的。
contacts_db = SqliteDatabase('contacts.db') class Person(Model): name = CharField() class Meta: database = contacts_db
在簡單model示例中,你會注意到,我們創(chuàng)建了一個定義數(shù)據(jù)庫的BaseModel,然后擴展了它。這是定義數(shù)據(jù)庫和創(chuàng)建模型的首選方法。
你可以通過 ModelClass._meta
來使用:
Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: type object 'Person' has no attribute 'Meta' >>> Person._meta <peewee.modeloptions object="" at="" 0x7f51a2f03790="">
ModelOptions
實現(xiàn)了幾個查看model metadata的方法:
{'id': <peewee.autofield object="" at="" 0x7f51a2e92750="">, 'name': <peewee.charfield object="" at="" 0x7f51a2f0a510="">} >>> Person._meta.primary_key <peewee.autofield object="" at="" 0x7f51a2e92750=""> >>> Person._meta.database <peewee.sqlitedatabase object="" at="" 0x7f519bff6dd0="">
Model
在ORM數(shù)據(jù)中就是一張表,那么表的屬性可以有如下選項。它們是被定義在Meta中元數(shù)據(jù)。
Option | Meaning | 是否可繼承? |
---|---|---|
database | 指定表創(chuàng)建依附的數(shù)據(jù)庫 | yes |
table_name | 表名 | no |
table_function | 生成表名的函數(shù) | yes |
indexes | 多行索引 | yes |
primary_key | 主鍵 | yes |
constraints | 表約束的列表 | yes |
schema | 模型的數(shù)據(jù)庫架構 | yes |
only_save_dirty | 調(diào)用model.save()時,僅保存臟字段,指定字段? | yes |
options | 創(chuàng)建表擴展的選項字典 | yes |
table_settings | 在右括號后設置字符串的列表 | yes |
temporary | 指示臨時表 | yes |
legacy_table_names | 使用舊表名生成(默認情況下啟用) | yes |
depends_on | 指示此表依賴于另一個表進行創(chuàng)建 | no |
without_rowid | 指示表不應具有rowid(僅限SQLite) | no |
strict_tables | 指示嚴格的數(shù)據(jù)類型(僅限SQLite,3.37+) | yes |
四、Filed 字段
Field
類是用來將Model屬性映射到數(shù)據(jù)庫列。每個字段類型都有一個相應的SQL存儲類,將python
數(shù)據(jù)類型轉(zhuǎn)化為基本的存儲類型。
當創(chuàng)建Model類時,fields被定義成類的屬性。它看起來和django的數(shù)據(jù)庫框架很類似。
class User(Model): username = CharField() join_date = DateTimeField() about_me = TextField()
在上面的例子中,因為沒有field有主鍵屬性primary_key=True,所以會創(chuàng)建一個名字是id的自增主鍵。
peewee中可用的字段包括:
字段類型 | Sqlite | Postgresql | MySQL |
---|---|---|---|
AutoField | integer | serial | integer |
BigAutoField | integer | bigserial | bigint |
IntegerField | integer | integer | integer |
BigIntegerField | integer | bigint | bigint |
SmallIntegerField | integer | smallint | smallint |
IdentityField | not supported | int identity | not supported |
FloatField | real | real | real |
DoubleField | real | double precision | double precision |
DecimalField | decimal | numeric | numeric |
CharField | varchar | varchar | varchar |
FixedCharField | char | char | char |
TextField | text | text | text |
BlobField | blob | bytea | blob |
BitField | integer | bigint | bigint |
BigBitField | blob | bytea | blob |
UUIDField | text | uuid | varchar(40) |
BinaryUUIDField | blob | bytea | varbinary(16) |
DateTimeField | datetime | timestamp | datetime |
DateField | date | date | date |
TimeField | time | time | time |
TimestampField | integer | integer | integer |
IPField | integer | bigint | bigint |
BooleanField | integer | boolean | bool |
BareField | untyped | not supported | not supported |
ForeignKeyField | integer | integer | integer |
1、字段初始化參數(shù)
所有字段類型接受的參數(shù)及其默認值
null = False
允許空值index = False
創(chuàng)建索引unique = False
創(chuàng)建唯一索引column_name = None
顯式指定數(shù)據(jù)庫中的列名default = None
默認值,可以使任意值或可調(diào)用對象primary_key = False
指明主鍵constraints = None
約束條件sequence = None
序列名字(如果數(shù)據(jù)庫支持)collation = None
排序字段unindexed = False
虛表上的字段不應該被索引choices = None
兩種可選項:value display
help_text = None
幫助說明字段。表示此字段的任何有用文本的字符串verbose_name = None
表示此字段的用戶友好名稱的字符串index_type = None
索引類型
2、字段特有參數(shù)
在一些字段中有些自己特有的參數(shù),如下:
字段類型 | 特有參數(shù) |
---|---|
CharField | max_length |
FixedCharField | max_length |
DateTimeField | formats |
DateField | formats |
TimeField | formats |
TimestampField | resolution, utc |
DecimalField | max_digits, decimal_places, auto_round, rounding |
ForeignKeyField | model, field, backref, on_delete, on_update, deferrable lazy_load |
BareField | adapt |
3、字段默認參數(shù)
peewee
可以為每一個字段提供默認值,比如給intergerField 默認值0而不是NULL。你可以申明字段時指定默認值:
class Message(Model): context = TextField() read_count = IntegerField(default=0)
在某些情況下,默認值是動態(tài)的會更有意義。一個可能的場景就是當前時間。Peewee
允許您在這些情況下指定一個函數(shù),該函數(shù)的返回值將在創(chuàng)建對象時使用。注意,使用時只提供了函數(shù),并不需要實際調(diào)用它。
class Message(Model): context = TextField() timestamp = DateTimeField(default=datetime.datetime.now)
如果你正在使用一個接受可變類型(list, dict等)的字段,并想提供一個默認值。將默認值包裝在一個簡單的函數(shù)中是個好主意,這樣,多個模型實例就不會共享對同一底層對象的引用。
def house_defaults(): return {'beds': 0, 'baths': 0} class House(Model): number = TextField() street = TextField() attributes = JSONField(default=house_defaults)
4、索引
peewee可以通過單列索引和多列索引??蛇x地包括UNIQUE
約束。Peewee還支持對模型和字段的用戶定義約束。
單列索引
單列索引使用字段初始化參數(shù)定義。下面的示例在用戶名字段上添加一個惟一索引,在電子郵件字段上添加一個普通索引
class User(Model): username = CharField(unique=True) email = CharField(index=True)
在列上添加用戶定義的約束。你可以使用constraints參數(shù)。例如,您可能希望指定一個默認值,或者添加一個CHECK約束
class Product(Model): name = CharField(unique=True) price = DecimalField(constraints=[Check('price < 10000')]) created = DateTimeField( constraints=[SQL("DEFAULT (datetime('now'))")])
多列索引
可以使用嵌套元組將多列索引定義為元屬性。每個表的索引是一個2元組,第一部分是索引字段名稱的元組,可以有多個字段,第二部分是一個布爾值,指示索引是否應該唯一。
class Transaction(Model): from_acct = CharField() to_acct = CharField() amount = DecimalField() date = DateTimeField() class Meta: indexes = ( # create a unique on from/to/date (('from_acct', 'to_acct', 'date'), True), # create a non-unique on from/to (('from_acct', 'to_acct'), False), )
記住,如果索引元組只包含一項,則添加末尾逗號
五、基本操作 增刪改查
peewee
中關于增刪改查的基本操作方法如下:
增 :
- create():最常用創(chuàng)建,返回創(chuàng)建實例
- save():第一次執(zhí)行的save是插入,第二次是修改
- insert: 插入數(shù)據(jù),不創(chuàng)建數(shù)據(jù)庫實例。返回id
- insert_many: 批量插入
- bulk_create:批量插入,類似于insert_many。可指定單次插入的數(shù)量
- batch_commit: 自動添加了一個事務,然后一條條的插入
- insert_from: 從另一個表中查詢的數(shù)據(jù)作為插入的數(shù)據(jù)
刪除 :
- delete().where().execute()
- delete_instance() 直接執(zhí)行刪除了,不用調(diào)用execute() 方法
修改 :
- save(): 第一次執(zhí)行的save是插入,第二次是修改
- update() 用于多字段更新
查詢 :
- Model.get(): 檢索與給定查詢匹配的單個實例。報 Model.DoesNotExist 異常。如果有多條記錄滿足條件,則返回第一條
- get_or_none() :與get使用方法相同。區(qū)別是找不到結(jié)果時不會報錯
- get_by_id() :通過主鍵查找,是一種快捷方式
- Model['id_num']: 和上面的get_by_id一樣是通過主鍵查找。
- get_or_create(): 首先查詢,如果查不到將創(chuàng)建一個新的記錄
- select() 查詢多條數(shù)據(jù)
1、創(chuàng)建
單條插入
你可以用 Model.create()
創(chuàng)建一個新的實例。這個方法接收關鍵字參數(shù),參數(shù)要和表定義的字段一致。返回值是新的實例
>>> User.create(username='Charlie') <__main__.User object at 0x2529350>
批量插入
有幾種方法可以快速加載大量數(shù)據(jù),缺乏經(jīng)驗的做法是在循環(huán)中調(diào)用Model.create來創(chuàng)建
data_source = [ {'field1': 'val1-1', 'field2': 'val1-2'}, {'field1': 'val2-1', 'field2': 'val2-2'}, # ... ] for data_dict in data_source: MyModel.create(**data_dict)
上面的方法比較慢的原因有幾個:
- 如果沒有在事務中裝飾循環(huán),那么每個對
create
()的調(diào)用都發(fā)生在它自己的事務中。這將會非常緩慢 - 必須生成每個
InsertQuery
并將其解析為SQL - 需要原生SQL語句傳入到數(shù)據(jù)庫中解析
- 檢索最后一
個insert id
,這在某些情況下會導致執(zhí)行額外的查詢
可以通過一個簡單的裝飾: atomic
來大幅度提高速度
# This is much faster. with db.atomic(): for data_dict in data_source: MyModel.create(**data_dict)
上面的代碼仍然沒有解決2、3、4這三點。我們可以通過 insert_many
帶來一個大的速度提升。這個方法接收多列元組或字典,然后在一次SQL語句中插入多行數(shù)據(jù)。
data_source = [ {'field1': 'val1-1', 'field2': 'val1-2'}, {'field1': 'val2-1', 'field2': 'val2-2'}, # ... ] # Fastest way to INSERT multiple rows. MyModel.insert_many(data_source).execute()
insert_many()
方法還接收多行元組,同時需要提供一個對應的字段。
# We can INSERT tuples as well... data = [('val1-1', 'val1-2'), ('val2-1', 'val2-2'), ('val3-1', 'val3-2')] # But we need to indicate which fields the values correspond to. MyModel.insert_many(data, fields=[MyModel.field1, MyModel.field2]).execute()
在裝飾中批量插入是一個好的方法。
# You can, of course, wrap this in a transaction as well: with db.atomic(): MyModel.insert_many(data, fields=fields).execute()
插入大量數(shù)據(jù)
在大量數(shù)據(jù)的插入場景下,根據(jù)數(shù)據(jù)源中的行數(shù),您可能需要將其分解為多個塊。SQLite
通常有999
或32766
的限制
您可以編寫一個循環(huán)來將數(shù)據(jù)批處理成塊(在這種情況下,強烈建議您使用事務)
# Insert rows 100 at a time. with db.atomic(): for idx in range(0, len(data_source), 100): MyModel.insert_many(data_source[idx:idx+100]).execute()
peewwee
提供了一個chunked
函數(shù)幫助你高效的將普通可迭代對象拆分成為可批處理對象。
from peewee import chunked # Insert rows 100 at a time. with db.atomic(): for batch in chunked(data_source, 100): MyModel.insert_many(batch).execute()
Model.bulk_create()
的行為有點像insert_many()
,但是可以用來插入沒有保存的數(shù)據(jù)庫實例,并且可以指定每次插入的數(shù)量。如一共插入345,如果指定了一次插入100條記錄,那么就是4次插入,3 * 100 + 1 * 45
什么叫沒有保存的數(shù)據(jù)庫實例呢?就是類似于 User(username='kk')
,創(chuàng)建的數(shù)據(jù)庫實例。
# Read list of usernames from a file, for example. with open('user_list.txt') as fh: # Create a list of unsaved User instances. users = [User(username=line.strip()) for line in fh.readlines()] # Wrap the operation in a transaction and batch INSERT the users # 100 at a time. with db.atomic(): User.bulk_create(users, batch_size=100)
bulk_update()
和 bulk_create
類似,可以用來插入沒有保存的數(shù)據(jù)庫實例,自動添加了一個事務,然后一條條的插入
# List of row data to insert. row_data = [{'username': 'u1'}, {'username': 'u2'}, ...] # Assume there are 789 items in row_data. The following code will result in # 8 total transactions (7x100 rows + 1x89 rows). for row in db.batch_commit(row_data, 100): User.create(**row)
從另一個表批量裝載
Model.insert_from()
如果要批量插入的數(shù)據(jù)存儲在另一個表中,還可以創(chuàng)建源為SELECT查詢的INSERT查詢。
res = (TweetArchive .insert_from( Tweet.select(Tweet.user, Tweet.message), fields=[TweetArchive.user, TweetArchive.message]) .execute())
2、刪除
要刪除單個模型實例,可以使用model.delete_instance()快捷方式。delete_instance()將刪除給定的模型實例,并且可以選擇遞歸地刪除任何依賴對象(通過指定recursive=True)。
刪除一個記錄:Model.delete_instance()
刪除任意記錄:Model.delete()
3、更新
save()
:單個更新
一旦模型實例有了主鍵,隨后對save()的任何調(diào)用都將導致一個UPDATE而不是另一個INSERT。模型的主鍵不會改變
>>> user.save() # save() returns the number of rows modified. 1 >>> user.id 1 >>> user.save() >>> user.id 1 >>> huey.save() 1 >>> huey.id 2
update
:批量更新
接受關鍵字參數(shù),其中鍵對應于模型的字段名稱
>>> today = datetime.today() >>> query = Tweet.update(is_published=True).where(Tweet.creation_date < today) >>> query.execute() # Returns the number of rows that were updated. 4
4、查詢
單條記錄查詢
你可以通過Model.get()
方法查詢到給條件的數(shù)據(jù)。如果是通過主鍵查找,也可以用一個快捷方法 Model.get_by_id()。
此方法是使用給定查詢調(diào)用Model.select()
的快捷方式,但將結(jié)果集限制為一行。需要注意的是使用get()方法,如果沒有找到匹配的數(shù)據(jù)會拋出錯誤:DoesNotExist
get
>>> User.get(User.id == 1) <__main__.User object at 0x25294d0> >>> User.get_by_id(1) # Same as above. <__main__.User object at 0x252df10> >>> User[1] # Also same as above. <__main__.User object at 0x252dd10> >>> User.get(User.id == 1).username u'Charlie' >>> User.get(User.username == 'Charlie') <__main__.User object at 0x2529410> >>> User.get(User.username == 'nobody') UserDoesNotExist: instance matching query does not exist: SQL: SELECT t1."id", t1."username" FROM "user" AS t1 WHERE t1."username" = ? PARAMS: ['nobody']
單條記錄查詢方法:
- Model.get()
- Model.get_by_id()
- Model.get_or_none() - if no matching row is found, return None.
- Model.select()
- SelectBase.get()
- SelectBase.first() - return first record of result-set or None.
查詢或創(chuàng)建:
Model.get_or_create() 它首先嘗試檢索匹配的行。如果失敗,將創(chuàng)建一個新行。
通常,可以依賴唯一約束或主鍵來防止創(chuàng)建重復對象。例如,假設我們希望使用示例用戶模型實現(xiàn)注冊新用戶帳戶。用戶模型對用戶名字段有唯一的約束,因此我們將依賴數(shù)據(jù)庫的完整性保證,以確保不會出現(xiàn)重復的用戶名:
try: with db.atomic(): return User.create(username=username) except peewee.IntegrityError: # `username` is a unique column, so this username already exists, # making it safe to call .get(). return User.get(User.username == username)
上面的例子首先嘗試創(chuàng)建,然后回退到查詢,依靠數(shù)據(jù)庫來強制執(zhí)行唯一約束。
如果您希望首先嘗試檢索記錄,可以使用get_or_create()。
該函數(shù)返回一個2元組,其中包含實例和一個布爾值,該值指示對象是否被創(chuàng)建。
user, created = User.get_or_create(username=username) person, created = Person.get_or_create( first_name=first_name, last_name=last_name, defaults={'dob': dob, 'favorite_color': 'green'})
查詢多行記錄:
可以通過Model.select()
獲取多行數(shù)據(jù)。peewee允許你迭代這些數(shù)據(jù),同時也可以索引和切片。
>>> query = User.select() >>> [user.username for user in query] ['Charlie', 'Huey', 'Peewee'] >>> query[1] <__main__.User at 0x7f83e80f5550> >>> query[1].username 'Huey' >>> query[:2] [<__main__.User at 0x7f83e80f53a8>, <__main__.User at 0x7f83e80f5550>]
select()是
很智能的,在查詢一次的前提下可以多次迭代,切片,下標取值等。
在緩存結(jié)果時,同一查詢的后續(xù)迭代不會命中數(shù)據(jù)庫。要禁用此行為(以減少內(nèi)存使用),請在迭代時調(diào)用Select.iterator()。
除了返回模型實例外,Select查詢還可以返回字典、元組和命名元組。根據(jù)您的用例,您可能會發(fā)現(xiàn)將行作為字典使用更容易
>>> query = User.select().dicts() >>> for row in query: ... print(row) {'id': 1, 'username': 'Charlie'} {'id': 2, 'username': 'Huey'} {'id': 3, 'username': 'Peewee'}
iterator() :
不緩存查詢結(jié)果
默認情況下,peewee將緩存迭代Select查詢時返回的行。這是一種優(yōu)化,允許多次迭代以及索引和切片,而不會導致額外的查詢。但是,當您計劃在大量行上進行迭代時,這種緩存可能會有問題。
為了減少內(nèi)存的消耗,使用iterator()方法。這個方法允許返回結(jié)果不緩存數(shù)據(jù)。使用更少的內(nèi)存。
stats = Stat.select() # Our imaginary serializer class serializer = CSVSerializer() # Loop over all the stats and serialize. for stat in stats.iterator(): serializer.serialize_object(stat)
對于簡單的查詢,您可以通過將行作為字典返回來進一步提高速度。namedtuples或元組。以下方法可用于任何Select查詢,以更改結(jié)果行類型。
dicts()
namedtuples()
tuples()
objects :
將多個查詢表放在一個實例中
當對包含多個表中的列的大量行進行迭代時,peewee將為返回的每一行構建查詢模型。對于復雜查詢,此操作可能很慢。例如,如果我們選擇一個tweet列表以及tweet作者的用戶名和頭像,Peewee必須為每一行創(chuàng)建兩個對象(tweet和用戶)。除了上述行類型之外,還有第四個方法objects(),它將作為模型實例返回行,但不會分解模型查詢。
query = (Tweet .select(Tweet, User) # Select tweet and user data. .join(User)) # Note that the user columns are stored in a separate User instance # accessible at tweet.user: for tweet in query: print(tweet.user.username, tweet.content) # Using ".objects()" will not create the tweet.user object and assigns all # user attributes to the tweet instance: for tweet in query.objects(): print(tweet.username, tweet.content)
為了獲得最佳性能,您可以執(zhí)行查詢,然后使用底層數(shù)據(jù)庫游標對結(jié)果進行迭代。
Database.execute()。
接受查詢對象,執(zhí)行查詢,并返回DB-API 2.0游標對象。光標將返回原始行元組:
query = Tweet.select(Tweet.content, User.username).join(User) cursor = database.execute(query) for (content, username) in cursor: print(username, '->', content)
5、事務
數(shù)據(jù)庫事務
(Transaction)
是一種機制,包含了一組數(shù)據(jù)庫操作命令
事務把所有的命令作為一個整體一起向系統(tǒng)提交或撤銷操作請求,即這一組數(shù)據(jù)庫命令要么都執(zhí)行,要么都不執(zhí)行,因此事務是一個不可分割的工作邏輯單元。
事務具有 4 個特性,即原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durability
),這 4 個特性通常簡稱為 ACID。
peewee事務
Peewee實現(xiàn)事務的方法是 Database.atomic()
方法,非常簡單
當事務執(zhí)行成功之后,它會自動commit(),不需要我們手動調(diào)。當事務的代碼塊中拋出異常時,它會自動調(diào)用rollback(),將數(shù)據(jù)庫狀態(tài)恢復到操作之前,保證要么命令全部執(zhí)行,要么全部不執(zhí)行。
Peewee中實現(xiàn)事務有兩種使用方式,一種是將atomic當做Context manager使用,另外一種將atomic當修飾器使用。
Context manager
with db.atomic(): for data_dict in data_source: MyModel.create(**data_dict)
裝飾器
@db.atomic() def insert_data() for data_dict in data_source: MyModel.create(**data_dict)
事務其他特性:
- 除了自動commit()和rollback()之外,也可以手動調(diào)用commit()和rollback()方法
- 事務支持嵌套使用
- 在一個事務中對數(shù)據(jù)庫操作能夠有效減少事務的耗時,增加操作效率
6、過濾
您可以使用普通的python操作符過濾特定的記錄。
>>> user = User.get(User.username == 'Charlie') >>> for tweet in Tweet.select().where(Tweet.user == user, Tweet.is_published == True): ... print(tweet.user.username, '->', tweet.message) ... Charlie -> hello world Charlie -> this is fun >>> for tweet in Tweet.select().where(Tweet.created_date < datetime.datetime(2011, 1, 1)): ... print(tweet.message, tweet.created_date) ... Really old tweet 2010-01-01 00:00:00 ... print(tweet.message) hello world this is fun look at this picture of my food
7、記錄分類
給返回的數(shù)據(jù)排序,可以使用order_by
普通使用:
>>> for t in Tweet.select().order_by(Tweet.created_date): ... print(t.pub_date)
倒序排列:
可以使用desc或者 - 號
Tweet.select().order_by(Tweet.created_date.desc()) Tweet.select().order_by(-Tweet.created_date) # Note the "-" prefix.
正序排列:
User.select().order_by(+User.username)
高級使用:
對計算值進行排序時,可以包括必要的SQL表達式,也可以引用指定給該值的別名。
query = (User .select(User.username, fn.COUNT(Tweet.id).alias('num_tweets')) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User.username))
您可以使用select子句中使用的相同計數(shù)表達式進行訂購。在下面的示例中,我們按tweet ID的COUNT()降序排序:
query = (User .select(User.username, fn.COUNT(Tweet.id).alias('num_tweets')) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User.username) .order_by(fn.COUNT(Tweet.id).desc()))
或者,可以在select
子句中引用指定給計算值的別名。這種方法的優(yōu)點是易于閱讀。請注意,我們不是直接引用命名別名,而是使用SQL幫助程序?qū)ζ溥M行包裝:
query = (User .select(User.username, fn.COUNT(Tweet.id).alias('num_tweets')) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User.username) .order_by(SQL('num_tweets').desc()))
同樣,也可以使用如上
ntweets = fn.COUNT(Tweet.id) query = (User .select(User.username, ntweets.alias('num_tweets')) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User.username) .order_by(ntweets.desc())
8、計數(shù)
可以使用count來計算返回數(shù)量
>>> Tweet.select().count() 100 >>> Tweet.select().where(Tweet.id > 50).count() 50
9、分頁
paginate()
方法可以很簡單的獲取一個分頁的數(shù)據(jù)。paginate有兩個參數(shù):page_number 和 items_per_page。第一個參數(shù)是取回數(shù)據(jù)的頁數(shù);第二個參數(shù)是每一頁多少元素。這兩個參數(shù)加起來才能完成分頁
>>> for tweet in Tweet.select().order_by(Tweet.id).paginate(2, 10): ... print(tweet.message) ... tweet 10 tweet 11 tweet 12 tweet 13 tweet 14 tweet 15 tweet 16 tweet 17 tweet 18 tweet 19
分頁的功能也可以用 limit() 和 offset() 來實現(xiàn)
Tweet.select().order_by(Tweet.id).offset(10).limit(10)
offset(10) 跳過10個記錄
limit(10) 取10個記錄
六、聚合查詢
聚合查詢:對查詢出來的結(jié)果進一步處理,包括統(tǒng)計,分組,求最大值,求平均值等。
聚合常用的函數(shù):
- COUNT:計算表中的記錄數(shù)(行數(shù))
- SUM:計算表中數(shù)值列中數(shù)據(jù)的合計值
- AVG:計算表中數(shù)值列中數(shù)據(jù)的平均值
- MAX:求出表中任意列中數(shù)據(jù)的最大值
- MIN:求出表中任意列中數(shù)據(jù)的最小值
用于匯總的函數(shù)稱為聚合函數(shù)或者聚集函數(shù)。所謂聚合,就是將多行匯總為一行。實際上,所有的聚合函數(shù)都是這樣,輸入多行輸出一行。
聚合函數(shù)的使用:
mysql> select * from person; +----+------+-----+------+-----+ | id | name | age | High | sex | +----+------+-----+------+-----+ | 1 | ljk | 30 | 177 |1 | | 2 | aghj | 23 | 168 |1 | +----+------+-----+------+-----+ 2 rows in set (0.00 sec) ************************************ * 聚合函數(shù) * ************************************ mysql> select count(*) from person; +----------+ | count(*) | +----------+ | 2 | +----------+ 1 row in set (0.00 sec) ---------------------------------------- mysql> select sum(age) from person; +----------+ | sum(age) | +----------+ | 53 | +----------+ 1 row in set (0.00 sec) ---------------------------------------- mysql> select avg(high) from person; +-----------+ | avg(high) | +-----------+ | 172.5000 | +-----------+ 1 row in set (0.00 sec) ---------------------------------------- mysql> select max(high) from person; +-----------+ | max(high) | +-----------+ | 177 | +-----------+ 1 row in set (0.00 sec) mysql> select * from person; +----+------+-----+------+-----+ | id | name | age | High | sex | +----+------+-----+------+-----+ | 1 | ljk | 30 | 177 |1 | | 2 | aghj | 23 | 168 |1 | | 3 | 0 | 22 | 165 |0 | +----+------+-----+------+-----+ 3 rows in set (0.00 sec)
mysql> select avg(High) from person group by sex; +-----------+ | avg(High) | +-----------+ | 172.5000 | | 165.0000 | +-----------+ 2 rows in set (0.00 sec) # 使用having對分組的數(shù)據(jù)篩選 mysql> select avg(High) as high from person group by sex having high > 170; +----------+ | high | +----------+ | 172.5000 | +----------+ 1 row in set (0.00 sec)
where :
分組之前篩選數(shù)據(jù)
where 子句的作用是在對查詢結(jié)果進行分組前,將不符合where條件的行去掉,即在分組之前過濾數(shù)據(jù),where條件中不能包含聚組函數(shù),使用where條件過濾出特定的行。
having :
對分組之后篩選分組的數(shù)據(jù)
having 子句的作用是篩選滿足條件的組,即在分組之后過濾數(shù)據(jù),條件中經(jīng)常包含聚組函數(shù),使用having 條件過濾出特定的組,也可以使用多個分組標準進行分組。
總結(jié)一下過濾的順序
on->join->where->group by->having
分組
查詢用戶以及每個人擁有的tweet賬號數(shù)量。這里使用了group_by,將結(jié)果根據(jù)User表分類。
query = (User .select(User, fn.Count(Tweet.id).alias('count')) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User))
假設有如下數(shù)據(jù)庫,一個多對多的關系。
class Photo(Model): image = CharField() class Tag(Model): name = CharField() class PhotoTag(Model): photo = ForeignKeyField(Photo) tag = ForeignKeyField(Tag)
查詢Tag記錄,按照Tag分組,篩選出每組Tag里Photo數(shù)量超過5個的記錄。
query = (Tag .select() .join(PhotoTag) .join(Photo) .group_by(Tag) .having(fn.Count(Photo.id) > 5))
HAVING 子句可以讓我們篩選分組后的各組數(shù)據(jù)。
HAVING,它與 GROUP BY 配合使用,為聚合操作指定條件。
WHERE 子句只能指定行的條件,而不能指定組的條件。所以當數(shù)據(jù)分組之后就需要 HAVING 對分組的數(shù)據(jù)篩選。
具體區(qū)別:
where 用在group_by前,having用在group_by之后。
聚合函數(shù)(avg、sum、max、min、count),不能作為條件放在where之后,但可以放在having之后
七、Scalar
對查詢出來的數(shù)據(jù)做處理
可以通過調(diào)用Query.scalar()來檢索標量值。例如
>>> Employee.select( ... fn.Min(Employee.salary), fn.Max(Employee.salary) ... ).scalar(as_tuple=True) (30000, 50000)
您可以通過傳遞來檢索多個標量值
>>> Employee.select( ... fn.Min(Employee.salary), fn.Max(Employee.salary) ... ).scalar(as_tuple=True) (30000, 50000)
八、窗口
窗口函數(shù)是指對作為SELECT查詢一部分處理的數(shù)據(jù)滑動窗口進行操作的聚合函數(shù)。窗口功能可以執(zhí)行以下操作:
對結(jié)果集的子集執(zhí)行聚合。
計算一個運行總數(shù)。
排名結(jié)果。
將行值與前面(或后面?。┬兄械闹颠M行比較。
peewee支持SQL窗口函數(shù),可以通過調(diào)用Function.over()并傳入分區(qū)或排序參數(shù)來創(chuàng)建這些函數(shù)。
九、復雜篩選
peewee支持以下類型的比較
1、查詢中支持的篩選運算符
Comparison | Meaning |
---|---|
== | x equals y |
< | x is less than y |
<= | x is less than or equal to y |
> | x is greater than y |
>= | x is greater than or equal to y |
!= | x is not equal to y |
<< | x IN y, where y is a list or query |
>> | x IS y, where y is None/NULL |
% | x LIKE y where y may contain wildcards |
** | x ILIKE y where y may contain wildcards |
^ | x XOR y |
~ | Unary negation (e.g., NOT x) |
2、篩選方法
因為用完了要重寫的操作符,所以有一些額外的查詢操作可以作為方法使用
Method | Meaning |
---|---|
.in_(value) | 查詢在范圍內(nèi) |
.not_in(value) | 查詢不在范圍內(nèi) |
.is_null(is_null) | 為空或不為空。接受布爾參數(shù) |
.contains(substr) | 通配符搜索子字符串 |
.startswith(prefix) | 查詢以prefix開頭的數(shù)據(jù) |
.endswith(suffix) | 查詢以prefix結(jié)尾的數(shù)據(jù) |
.between(low, high) | 查詢在low和high中間的值 |
.regexp(exp) | 正則表達式匹配匹配的數(shù)據(jù),貪婪模式 |
.iregexp(exp) | 正則表達式匹配匹配的數(shù)據(jù),非貪婪模式 |
.bin_and(value) | 二進制加 |
.bin_or(value) | 二進制或 |
.concat(other) | Concatenate two strings or objects using ||. |
.distinct() | 標記重復的數(shù)據(jù) |
.collate(collation) | 指定具有給定排序規(guī)則的列 |
.cast(type) | 將列的值強制轉(zhuǎn)換為給定類型 |
3、聯(lián)合查詢邏輯操作
使用邏輯操作的聯(lián)合查詢
Operator | Meaning | Example |
---|---|---|
& | AND | (User.is_active == True) & (User.is_admin == True) |
| | OR | (User.is_admin) | (User.is_superuser) |
~ | NOT (unary negation) | ~(User.username.contains('admin')) |
# Find the user whose username is "charlie". User.select().where(User.username == 'charlie') # Find the users whose username is in [charlie, huey, mickey] User.select().where(User.username.in_(['charlie', 'huey', 'mickey'])) Employee.select().where(Employee.salary.between(50000, 60000)) Employee.select().where(Employee.name.startswith('C')) Blog.select().where(Blog.title.contains(search_string))
請注意,實際的比較用括號括起來。 Python 的運算符優(yōu)先級要求將比較括在括號中。
# Find any users who are active administrations.
User.select().where(
(User.is_admin == True) &
(User.is_active == True))
可能你嘗試使用python語法中的in and or 和not操作,但是在查詢中是不生效的。所有的操作返回都是一個布爾值。
建議如下:
- 用 .in_() 和 .not_in() 替換 in和 not in
- 用&替換and
- 用|替換or
- 用~替換not
- 用.is_null()替換 is None 或 == None
十、SQL 方法
SQL方法,如 like , sum 等,可以通過 fn 來表達
從peewee中導入fn: from peewee import fn
query = (User .select(User, fn.COUNT(Tweet.id).alias('tweet_count')) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User) .order_by(fn.COUNT(Tweet.id).desc())) for user in query: print('%s -- %s tweets' % (user.username, user.tweet_count))
fn可以表達任何SQL方法,它的參數(shù)可以是字段,值,子查詢甚至嵌套函數(shù)
基礎使用
- fn.AVG() 返回指定列的平均值,NULL值不包括在計算中。
- fn.SUM() 返回指定列的數(shù)目,NULL值不包括在計算中。
- fn.MIN() 返回指定列的最小值,NULL值不包括在計算中。
- fn.MAX() 返回指定列的最大值,NULL值不包括在計算中。
- fn.DATE() 返回指定日期時間格式列的日期格式
- fn.DECIMAL(10, 2) ===> decimal(10,2)中的“2”表示小數(shù)部分的位數(shù)
進階使用
- fn.to_char() 返回指定列格式化后的字符串 e.g.: fn.to_char(18.88, '99.999') ===> 18.888; fn.to_char(model.field, '')。
- fn.char_length(str) 返回字符串字符數(shù)
- fn.array_agg() 接受一組值并返回一個數(shù)組。
- fn.array_agg(model.name).order_by(model.id.asc()) # array_agg(name order by id asc)
- fn.rank().over(partition_by=[field1, field2, or aggregation_field1], order_by=[fn.SUM(Booking.slots).desc()]) 實現(xiàn)rank() over(partition by filed order by filed)分區(qū)功能。
- fn.length() 返回指定列的長度。也可應用于order_by。e.g.: .order_by(fn.length(model.field).asc())。
- fn.CONCAT() 返回合并的字符串(CONCAT一定要大寫,小寫的concat用法不一樣)。fn.CONCAT(model.id, '-', model.name) ===> '188-張三'
1、SQL helper
有時,您可能想在sql中傳一些任意的sql語句。您可以使用特殊的SQL類來實現(xiàn)這一點
# We'll query the user table and annotate it with a count of tweets for # the given user query = (User .select(User, fn.Count(Tweet.id).alias('ct')) .join(Tweet) .group_by(User)) # Now we will order by the count, which was aliased to "ct" query = query.order_by(SQL('ct')) # You could, of course, also write this as: query = query.order_by(fn.COUNT(Tweet.id))
使用peewee執(zhí)行手工SQL語句有兩種方法
- Database.execute_sql() 用于執(zhí)行任何類型的查詢
- RawQuery 執(zhí)行SELECT查詢并返回模型實例
query = MyModel.raw('SELECT * FROM my_table WHERE data = %s', user_data) query.execute_sql()
2、安全和SQL注入
默認情況下,peewee將參數(shù)化查詢,因此用戶傳入的任何參數(shù)都將被轉(zhuǎn)義。
請確保將任何用戶定義的數(shù)據(jù)作為查詢參數(shù)傳入,而不是作為實際SQL查詢的一部分傳入:
query = MyModel.raw('SELECT * FROM my_table WHERE data = %s' % (user_data,)) # Good. `user_data` will be treated as a parameter to the query. query = MyModel.raw('SELECT * FROM my_table WHERE data = %s', user_data) # Bad! DO NOT DO THIS! query = MyModel.select().where(SQL('Some SQL expression %s' % user_data)) # Good. `user_data` will be treated as a parameter. query = MyModel.select().where(SQL('Some SQL expression %s', user_data))
MySQL和Postgresql
使用“%s”表示參數(shù)。另一方面,SQLite使用“?”。請確保使用適合數(shù)據(jù)庫的字符。還可以通過檢查Database.param
來查找此參數(shù)。
到此這篇關于python輕量級orm框架 peewee常用功能速查詳情的文章就介紹到這了,更多相關python輕量級orm框架 peewee常用功能速查內(nèi)容請搜索本站以前的文
版權聲明:本站文章來源標注為YINGSOO的內(nèi)容版權均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務器上建立鏡像,否則將依法追究法律責任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學習參考,不代表本站立場,如有內(nèi)容涉嫌侵權,請聯(lián)系alex-e#qq.com處理。