快速启动

本文简要介绍了Peewee的主要功能。本指南将涵盖:

备注

如果你想要更多肉的,有一个关于 creating a "twitter"-style web app 使用Peewee和Flask框架。在项目中 examples/ 文件夹您可以找到更多独立的peewee示例,例如 blog app .

I strongly 建议打开交互式shell会话并运行代码。这样您就可以感觉到输入查询了。

模型定义

模型类、字段和模型实例都映射到数据库概念:

对象

对应于…

模型类

数据库表

字段实例

表上的列

模型实例

数据库表中的行

当使用Peewee启动项目时,通常最好从数据模型开始,通过定义一个或多个 Model 类别:

from peewee import *

db = SqliteDatabase('people.db')

class Person(Model):
    name = CharField()
    birthday = DateField()

    class Meta:
        database = db # This model uses the "people.db" database.

备注

Peewee将自动从类的名称推断数据库表名。您可以通过指定 table_name 内部“meta”类中的属性(与 database 属性)。要进一步了解peewee如何生成表名,请参阅 表名 部分。

还要注意,我们为模型命名 Person 而不是 People . 这是您应该遵循的一个惯例——即使表将包含多个人,我们总是使用单数形式命名类。

有很多 field types 适用于存储各种类型的数据。Peewee处理在 毕多德式的 值和数据库使用的值,因此您可以在代码中使用Python类型,而不必担心。

当我们使用 foreign key relationships . 这对于Peewee很简单:

class Pet(Model):
    owner = ForeignKeyField(Person, backref='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))
uncle_bob.save() # bob is now stored in the database
# Returns: 1

备注

当你调用 save() ,返回修改的行数。

您也可以通过拨打 create() 方法,返回模型实例:

grandma = Person.create(name='Grandma', birthday=date(1935, 3, 1))
herb = Person.create(name='Herb', birthday=date(1950, 5, 5))

要更新行,请修改模型实例并调用 save() 坚持改变。在这里,我们将更改奶奶的姓名,然后将更改保存到数据库中:

grandma.name = 'Grandma L.'
grandma.save()  # Update grandma's name in the database.
# Returns: 1

现在我们在数据库中存储了3个人。让我们给他们一些宠物。奶奶不喜欢家里的动物,所以她没有,但赫伯是个动物爱好者:

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')

在漫长的一生之后,手套会生病死亡。我们需要将他从数据库中删除:

herb_mittens.delete_instance() # he had a great life
# Returns: 1

备注

的返回值 delete_instance() 是从数据库中删除的行数。

鲍勃叔叔认为在赫伯家里有太多动物死亡,所以他采用了fido:

herb_fido.owner = uncle_bob
herb_fido.save()

检索数据

我们数据库的真正优势在于它允许我们通过 查询. 关系数据库非常适合进行即席查询。

获取单个记录

让我们从数据库中检索奶奶的记录。要从数据库中获取单个记录,请使用 Select.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)

# prints:
# Bob
# Grandma L.
# Herb

让我们列出所有的猫和它们的主人的名字:

query = Pet.select().where(Pet.animal_type == 'cat')
for pet in query:
    print(pet.name, pet.owner.name)

# prints:
# Kitty Bob
# Mittens Jr Herb

注意

前一个查询有一个大问题:因为我们正在访问 pet.owner.name 我们在原始查询中没有选择这个关系,Peewee需要执行一个额外的查询来检索宠物的主人。这种行为被称为 N+1 一般应该避免。

有关处理关系和联接的深入指南,请参阅 关系和连接 文档。

我们可以通过同时选择这两个选项来避免额外的查询 Pet 和 人, 添加一个 join.

query = (Pet
         .select(Pet, Person)
         .join(Person)
         .where(Pet.animal_type == 'cat'))

for pet in query:
    print(pet.name, pet.owner.name)

# prints:
# Kitty Bob
# Mittens Jr Herb

让我们把鲍勃所有的宠物都买下来:

for pet in Pet.select().join(Person).where(Person.name == 'Bob'):
    print(pet.name)

# prints:
# Kitty
# Fido

我们可以在这里做另一件很酷的事情来得到鲍勃的宠物。因为我们已经有了一个对象来代表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)

# prints:
# Fido
# Kitty

现在让我们列出所有人,从最年轻到最年长:

for person in Person.select().order_by(Person.birthday.desc()):
    print(person.name, person.birthday)

# prints:
# Bob 1960-01-15
# Herb 1950-05-05
# Grandma L. 1935-03-01

组合筛选器表达式

Peewee支持任意嵌套表达式。让我们让所有生日是的人:

  • 1940年以前(奶奶)

  • 1959年以后(鲍勃)

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)

# prints:
# Bob 1960-01-15
# Grandma L. 1935-03-01

现在让我们做相反的事情。生日在1940年至1960年之间(包括这两年)的人:

query = (Person
         .select()
         .where(Person.birthday.between(d1940, d1960)))

for person in query:
    print(person.name, person.birthday)

# prints:
# Herb 1950-05-05

聚合和预取

现在让我们列出所有人 and 他们有多少宠物:

for person in Person.select():
    print(person.name, person.pets.count(), 'pets')

# prints:
# Bob 2 pets
# Grandma L. 0 pets
# Herb 1 pets

我们又碰到了一个经典的例子 N+1 查询行为。在本例中,我们对 Person 原件返还 SELECT !我们可以通过执行 JOIN 并使用SQL函数聚合结果。

query = (Person
         .select(Person, fn.COUNT(Pet.id).alias('pet_count'))
         .join(Pet, JOIN.LEFT_OUTER)  # include people without pets.
         .group_by(Person)
         .order_by(Person.name))

for person in query:
    # "pet_count" becomes an attribute on the returned model instances.
    print(person.name, person.pet_count, 'pets')

# prints:
# Bob 2 pets
# Grandma L. 0 pets
# Herb 1 pets

备注

Peewee提供了一个神奇的助手 fn() ,可用于调用任何SQL函数。在上面的例子中, fn.COUNT(Pet.id).alias('pet_count') 会被翻译成 COUNT(pet.id) AS pet_count .

现在让我们列出所有的人和他们所有宠物的名字。你可能已经猜到了,这很容易变成另一个 N+1 如果我们不小心的话。

在深入到代码中之前,请考虑这个示例与前面的示例有何不同,我们在这里列出了所有宠物及其所有者的名称。宠物只能有一个主人,所以当我们从 PetPerson 总有一场比赛。我们加入时情况不同 PersonPet 因为一个人可能没有宠物或者他们可能有几个宠物。因为我们使用的是关系数据库,如果我们要从 PersonPet 然后每个有多只宠物的人都会被重复,每只宠物一次。

如下所示:

query = (Person
         .select(Person, Pet)
         .join(Pet, JOIN.LEFT_OUTER)
         .order_by(Person.name, Pet.name))
for person in query:
    # We need to check if they have a pet instance attached, since not all
    # people have pets.
    if hasattr(person, 'pet'):
        print(person.name, person.pet.name)
    else:
        print(person.name, 'no pets')

# prints:
# Bob Fido
# Bob Kitty
# Grandma L. no pets
# Herb Mittens Jr

通常这种类型的复制是不可取的。以适应更常见(和直观)的工作流,即列出一个人并附加 a list 在那个人的宠物中,我们可以使用一种特殊的方法 prefetch()

query = Person.select().order_by(Person.name).prefetch(Pet)
for person in query:
    print(person.name)
    for pet in person.pets:
        print('  *', pet.name)

# prints:
# Bob
#   * Kitty
#   * Fido
# Grandma L.
# Herb
#   * Mittens Jr

SQL函数

最后一个查询。这将使用SQL函数查找名称以大写或小写开头的所有人员 G:

expression = fn.Lower(fn.Substr(Person.name, 1, 1)) == 'g'
for person in Person.select().where(expression):
    print(person.name)

# prints:
# Grandma L.

这只是基础!您可以根据自己的喜好使查询变得复杂。检查文档 查询 更多信息。

数据库

数据库处理完毕,让我们关闭连接:

db.close()

在实际的应用程序中,有一些已建立的模式用于管理数据库连接生存期。例如,Web应用程序通常会在请求开始时打开连接,并在生成响应后关闭连接。A connection pool 有助于消除与启动成本相关的延迟。

要了解如何设置数据库,请参阅 数据库 文档,其中提供了许多示例。Peewee还支持 configuring the database at run-time 以及随时设置或更改数据库。

使用现有数据库

如果已经有了数据库,可以使用 Pwiz,模型发生器 . 例如,如果我有一个名为 charles_blog, 我可能会跑:

python -m pwiz -e postgresql charles_blog > blog_models.py

接下来呢?

这就是快速入门。如果您想查看完整的Web应用程序,请查看 示例应用程序 .