本文译自:http://docs.peewee-orm.com/en/latest/peewee/quickstart.html
以下为译文:
note: 如果你需要一个更详实的例子的话,可以参考使用 peewee 和 Flask 框架的一个教程: 创建一个 twitter 风格的 web app
我 强烈 推荐你打开一个解释器 shell 会话并运行本文的代码。这种方式可以让你直观的感受到查询操作的效果。
定义 Model¶
Model 类,Field 以及 model 实例与数据库的对应关系如下:
Thing | Corresponds to... |
---|---|
Model 类 | 数据库表 |
Field 实例 | 数据库表字段 |
Model 实例 | 数据库表一行数据 |
一般在项目中使用 peewee 的时候都会需要定义一个或多个 Model 类:
from peewee import *
db = SqliteDatabase('people.db')
class Person(Model):
name = CharField()
birthday = DateField()
is_relative = BooleanField()
class Meta:
database = db # 此 model 使用 "people.db" 数据库。
note: 你可能注意到了我们使用 Person 而不是 People 命名我们的 model。 这里有一个你应该遵守的约定——尽管这个表将会包含多个 people,但是我们总是使用 单数名词 来命名类名。
这里有一些用来指定数据存储类型的 field types . Peewee 会知道将 pythonic 结果转换为数据库识别的数据,所以你可以放心的在你的代码中使用 Python 类型。
在 peewee 中定义 外键关系 也是非常的容易的:
class Pet(Model):
owner = ForeignKeyField(Person, related_name='pets')
name = CharField()
animal_type = CharField()
class Meta:
database = db # 此 model 使用 "people.db" 数据库。
现在我们已经定义了一下 model 了,下面可以连接数据库了。 虽然没必要显示的打开连接,但是这是一个好的习惯,因为它能立即暴露数据库库连接相关的各种错误,而不是在其他某个时候执行第一次查询操作的时候才被发现。 同样的在你处理完以后显示的关闭连接也是一个好的习惯——比如,一个 web app 可能会在接收到一个请求时打开连接,然后再它发送响应的时候关闭连接。
>>> db.connect()
下面我们将创建用于存储数据的表结构。下面的操作将创建相关的列,索引,sequences 以及外键约束:
>>> db.create_tables([Person, Pet])
存储数据¶
让我们在数据库里存一些 people 数据。可以使用 save 和 create 方法来添加和更新 people 记录:
>>> from datetime import date
>>> uncle_bob = Person(name='Bob', birthday=date(1960, 1, 15), is_relative=True)
>>> uncle_bob.save() # bob 现在已经存到数据库里了
1
note: 当你调用 save 方法时,将返回生效的记录行数。
你也可以通过调用 create 方法来添加一个 person, 它将返回一个 model 实例。
>>> 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)
为了更新一行数据,只需修改 model 实例然后调用 save 方法保存变更就可以了。下面我们将更改 Grandma 的名字然后把变更保存到数据中:
>>> grandma.name = 'Grandma L.'
>>> grandma.save() # 更新数据库中 grandma 的名字
1
我们现在在数据库里已经有 3 个人了。让我们给他们一些 pet. 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() # 他有个非常棒的人生
1
note: delete_instance() 的返回值是被从数据库中移除的数据的总行数。
Uncle Bob 觉得太多的动物会弄脏 Herb 的屋子,因此他收养了 Fido:
>>> herb_fido.owner = uncle_bob
>>> herb_fido.save()
>>> bob_fido = herb_fido # 为了更明确,我们重命名了变量
检索数据¶
下面讲解如何通过查询的方式检索数据。关系型数据库非常适合进行特定的查询操作。
获取单条记录¶
让我们从数据库中检索 Grandma 的数据。使用 SelectQuery.get() 从数据库中获取单条记录。
>>> grandma = Person.select().where(Person.name == 'Grandma L.').get()
我们也可以使用简便的 Model.get() 来实现相同的功能:
>>> grandma = Person.get(Person.name == 'Grandma L.')
罗列记录¶
列出数据库中所有的 people 记录:
>>> 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 但是我们并没有在我们原始的查询中包含这个值, 为了获取该 pet 的所有者,peewee 将执行一次额外的查询。这种行为将导致名为 N + 1 的问题,并且通常情况下应该避免这种问题。
我们可以通过同时查询 Pet 和 Person 以及增加一个 join 的方式来避免这个额外的查询。
>>> 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 的 pet:
>>> for pet in Pet.select().join(Person).where(Person.name == 'Bob'):
... print pet.name
...
Kitty
Fido
我们能做的另一个非常酷的事情是获取 bob 所拥有的 pet. 因为我们已经有了一个表示 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
让我们按从年幼到年长的顺序列出所有的 people:
>>> 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
现在让我们列出所有的 people 以及他们各自的 pet 的一些信息:
>>> 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(): # 注意是调用的 `aggregate_rows()` 方法。
... 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
尽管我们单独创建了一个子查询,但是实际上 只执行了一条 查询语句。
最后,让我们再做一个复杂的查询。让我们列出所有生日满足如下条件的 people:
- 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 之间的 people:
>>> 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 开头的所有 people:
>>> 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 子句也都是可用的,比如:
查看文档中的 Querying 获取更多的信息。
作用于已存在的数据库¶
如果你已经拥有了一个数据库,你可以使用 pwiz, a model generator. 自动生成 peewee models。 比如,我有一个名字叫 charles_blog 的 postgresql 数据库,我就可以这样运行:
python -m pwiz -e postgresql charles_blog > blog_models.py
下一步?¶
这里的内容只是用于快速上手。 如果你想找一个更完整的 web app 的示例的话,可以看一下 Example app .
Comments