本文將簡單的介紹Peewee,一覽它的主要特點,主要涉及到:
- 模型定義
- 存儲數據
- 檢索數據
注意:如果你偏愛稍微高級一點的教程, peewee建立類似twitter應用 是一篇結合Flask框架與peewee的全面教程。
強烈 建議通過SHELL交互的方式執行命令,這種方式可以提高你輸入查詢的快感。
模型定義
模型類、字段以及模型實例與數據的對於關系:
peewee | 數據庫 |
---|---|
Model class | Database table |
Field instance | Column on a table |
Model instance | Row in a database table |
開始一個peewee的項目時,通常最好通過定義一個或多個 Model
classes: 來開始你的數據模型。
from peewee import * db = SqliteDatabase('people.db') class Person(Model): name = CharField() birthday = DateField() is_relative = BooleanField() class Meta: database = db # This model uses the "people.db" database.
Note
注意我們使用 Person
替代了 People
來命名我們的模型,你應該遵循這個約定——即使該表包含多個人,我們總是使用單數的形式來命名模型類。
有許多 field types 適用於存儲各種類型的數據,Peewee可以自動的轉換python中的值到數據庫中使用,所以你不用擔心代碼中Python的類型。
當我們通過使用 外鍵 來創建關聯模型的時候非常有趣,使用peewee將變得非常簡單:
class Pet(Model): owner = ForeignKeyField(Person, related_name='pets') name = CharField() animal_type = CharField() class Meta: database = db # this model uses the "people.db" database
現在我們有了模型,接下來連接數據庫吧,雖然沒有必要建立顯示連接,這是個好的習慣,一旦發現連接數據發送錯誤,便可以排除之后的查詢錯誤,當處理完畢后應當關閉連接。比如:一個web應用程序當收到請求的時候可能會打開一個連接,響應后將關閉連接。
>>> db.connect()
然后我們在數據庫中傳接用於存儲我們數據的表,這將創建具有相應列、索引、自增鍵以及外鍵:
>>> db.create_tables([Person, Pet])
存儲數據
接下來我們用一些人填充數據庫吧,可以使用 save()
與 create()
方法添加和更新人的記錄。
>>> from datetime import date >>> uncle_bob = Person(name='Bob', birthday=date(1960, 1, 15), is_relative=True) >>> uncle_bob.save() # bob is now stored in the database 1
Note
當你調用 save()
的時候,將返回影響到的行數。
你也可以調用 create()
方法新增一個人,它將返回一個模型實例:
>>> grandma = Person.create(name='Grandma', birthday=date(1935, 3, 1), is_relative=True) >>> herb = Person.create(name='Herb', birthday=date(1950, 5, 5), is_relative=False)
為了更新一行,可以修改模型實例,調用 save()
來保存更改。這里我們將改變Grandma名字,然后存儲到數據庫:
>>> grandma.name = 'Grandma L.' >>> grandma.save() # Update grandma's name in the database. 1
現在我們已經在數據庫中保存了3個人了,接下來給他們一些寵物吧。Grandma不喜歡寵物在屋子里亂跑,所以她一只都沒有,但Herb是個寵物達人:
>>> bob_kitty = Pet.create(owner=uncle_bob, name='Kitty', animal_type='cat') >>> herb_fido = Pet.create(owner=herb, name='Fido', animal_type='dog') >>> herb_mittens = Pet.create(owner=herb, name='Mittens', animal_type='cat') >>> herb_mittens_jr = Pet.create(owner=herb, name='Mittens Jr', animal_type='cat')
在經歷很長的一段生活后,Mittens生病死了,我們需要將它從數據庫中移出:
>>> herb_mittens.delete_instance() # he had a great life 1
Note
delete_instance()
返回所有影響到的刪除行數。
Bob叔叔考慮到Herb家已經死了許多寵物,決定收養Fido:
>>> herb_fido.owner = uncle_bob >>> herb_fido.save() >>> bob_fido = herb_fido # rename our variable for clarity
檢索數據
數據庫的強大之處在於允許我們通過 查詢 來檢索數據,關系型數據庫針對點對點查詢非常優異。
獲取單條數據
讓我們從數據庫中獲取Grandma的數據,使用 SelectQuery.get()
來從數據庫中獲取單條數據:
>>> grandma = Person.select().where(Person.name == 'Grandma L.').get()
我們也可以等價簡寫成 Model.get()
:
>>> grandma = Person.get(Person.name == 'Grandma L.')
記錄列表
獲取數據庫中的所有人:
>>> for person in Person.select(): ... print person.name, person.is_relative ... Bob True Grandma L. True Herb False
獲取數據庫中所有貓以及主人的名字:
>>> query = Pet.select().where(Pet.animal_type == 'cat') >>> for pet in query: ... print pet.name, pet.owner.name ... Kitty Bob Mittens Jr Herb
上一個查詢是有問題的:因為我們訪問了 pet.owner.name
,但我們並沒有在原始查詢中進行查詢,peewee不得不進行一個額外的查詢來檢索寵物的主人,該行為請參考 N+1 ,通常這應當避免的。
為了避免額外的查詢,我們可以通過添加 join 來同時查詢 Pet 和 Person。
>>> query = (Pet ... .select(Pet, Person) ... .join(Person) ... .where(Pet.animal_type == 'cat')) >>> for pet in query: ... print pet.name, pet.owner.name ... Kitty Bob Mittens Jr Herb
獲取Bob的所有寵物
>>> for pet in Pet.select().join(Person).where(Person.name == 'Bob'): ... print pet.name ... Kitty Fido
我們還可以做另外一件很酷的事情來獲取Bob的寵物,一旦擁有可以代表Bob的對象,我們可以這樣替代:
>>> for pet in Pet.select().where(Pet.owner == uncle_bob): ... print pet.name
可以通過 order_by()
語句來排序:
>>> for pet in Pet.select().where(Pet.owner == uncle_bob).order_by(Pet.name): ... print pet.name ... Fido Kitty
獲取有所人,並且按照年齡從小到大排序:
>>> for person in Person.select().order_by(Person.birthday.desc()): ... print person.name, person.birthday ... Bob 1960-01-15 Herb 1950-05-05 Grandma L. 1935-03-01
獲取所有人 以及 他們寵物的一些信息:
>>> for person in Person.select(): ... print person.name, person.pets.count(), 'pets' ... for pet in person.pets: ... print ' ', pet.name, pet.animal_type ... Bob 2 pets Kitty cat Fido dog Grandma L. 0 pets Herb 1 pets Mittens Jr cat
我們再一次運行了典型的 N+1 查詢行為,可以進行 JOIN 操作並且合並起來:
>>> subquery = Pet.select(fn.COUNT(Pet.id)).where(Pet.owner == Person.id) >>> query = (Person ... .select(Person, Pet, subquery.alias('pet_count')) ... .join(Pet, JOIN.LEFT_OUTER) ... .order_by(Person.name)) >>> for person in query.aggregate_rows(): # Note the `aggregate_rows()` call. ... print person.name, person.pet_count, 'pets' ... for pet in person.pets: ... print ' ', pet.name, pet.animal_type ... Bob 2 pets Kitty cat Fido dog Grandma L. 0 pets Herb 1 pets Mittens Jr cat
甚至我們創建了單獨的子查詢,但只執行了 有且僅有一個 查詢。
最后我們處理一個復雜一點的,獲取所有人,只要他們的生日:
- 1940之前 (grandma)
- 1959之后 (bob)
>>> d1940 = date(1940, 1, 1) >>> d1960 = date(1960, 1, 1) >>> query = (Person ... .select() ... .where((Person.birthday < d1940) | (Person.birthday > d1960))) ... >>> for person in query: ... print person.name, person.birthday ... Bob 1960-01-15 Grandma L. 1935-03-01
現在在做一件相反的事情,獲取1940到1960之間的人:
>>> query = (Person ... .select() ... .where((Person.birthday > d1940) & (Person.birthday < d1960))) ... >>> for person in query: ... print person.name, person.birthday ... Herb 1950-05-05
最后一次查詢,使用SQL函數來檢索所有名字以 G 開頭的人,不區分大小寫:
>>> expression = (fn.Lower(fn.Substr(Person.name, 1, 1)) == 'g') >>> for person in Person.select().where(expression): ... print person.name ... Grandma L.
我們已經處理完數據庫,最后關閉連接:
>>> db.close()
這些都是基礎的使用,你可以進行更加復雜的操作。
所有其他的一些SQL語句也是可用的,比如:
group_by()
having()
limit()
與offset()
詳情請參考 Querying 文檔。
與現有的數據庫一起工作
如果你已經擁有了數據庫,可以使用 pwiz, a model generator 模型生成器自動生成peewee模型,例如,如果我有一個名為 charles_blog 的postgresql數據庫,我可能會運行:
python -m pwiz -e postgresql charles_blog > blog_models.py