
Peewee - lightweight, flexible and very fast ORM in Python
- Tutorial

I invite all djangists / alchemists to answer a little and read a free interpretation of the introductory tutorial and partially the Peewee documentation - stand-alone ORM, which is obligatory for acquaintance to any nutritionist and, in particular, to the flasker. Little is written about her, but in vain. It is very easy to make friends with Peewee, especially if you are already familiar with some ORM on ActiveRecord. More importantly, it’s nice to be friends with her :) Well, let's begin.
Installation
With pip:
From the repository:
Tests:
There is a binding for flask:
pip install peewee
From the repository:
git clone https://github.com/coleifer/peewee.git cd peewee python setup.py install
Tests:
python setup.py test
There is a binding for flask:
pip install flask-peewee
Definition of patterns or “smacks of junga”
All of the following code can be repeated one to one in an interactive interpreter or a separate script.
from peewee import *
db = SqliteDatabase('people.db')
class Person(Model):
name = CharField()
birthday = DateField()
is_relative = BooleanField()
class Meta:
database = db # модель будет использовать базу данных 'people.db'
There are many types of fields, for all occasions . Peewee takes on the conversion of Python objects to values suitable for the database, and vice versa.
Initializing arguments
Each field takes the following initialization arguments:
null=False
- is it possible to store null values;index=False
- whether to create an index for a given column in the database;unique=False
- whether to create a unique index for this column in the database. See also the chapter on composite indices ;verbose_name=None
- a string for human-readable representation of the field;help_text=None
- a line with auxiliary text for the field;db_column=None
- a string that explicitly sets the name of the column in the database for this field is used, for example, when working with the legacy database;default=None
- the default value for class fields during instantiation;choices=None
- a list or tuple of two-element tuples, where the first element is the value for the base, the second is the displayed value (similar to jang);primary_key=False
- whether to use this field as a primary key;sequence=None
- the sequence for filling the field (make sure that the backend supports this functionality);
Metadata
For each table, you can write uniform metadata in
class Meta
:Option | Description | Inherited? |
---|---|---|
database | model database | Yes |
db_table | name of the table in which data will be stored | not |
indexes | list of fields to index | Yes |
order_by | list of fields to sort by default | Yes |
primary_key | composite primary key, an instance of the CompositeKey class, example | Yes |
table_alias | alias tables for use in queries | not |
Let's try to set the relationship between the models through a foreign key. With peewee it is simple:
class Pet(Model):
owner = ForeignKeyField(Person, related_name='pets')
name = CharField()
animal_type = CharField()
class Meta:
database = db # модель будет использовать базу данных 'people.db'
Models are described, it remains to create the appropriate tables for them in the database:
>>> Person.create_table()
>>> Pet.create_table()
Work with data
For example, create a few people and get them pets:
>>> from datetime import date
>>> uncle_bob = Person(name='Bob', birthday=date(1960, 1, 15), is_relative=True)
>>> uncle_bob.save() # cохраним Боба в базе данных
Records can also be created directly using the Model.create () method without explicit save ():
>>> 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)
We will please the granny with the surname:
>>> grandma.name = 'Grandma L.'
>>> grandma.save() # обновим запись grandma
Now we’ll generate some living creatures. Granny is allergic to cats, but the coat of arms has some problems :
>>> 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')
At some point, Varezhka was tired of living with the Coat of Arms and, using the open window, he proudly ran into the sunset. Respecting his right to personal freedom, we will nevertheless delete the corresponding entry from the database:
>>> herb_mittens.delete_instance() # удачи, Варежка
1
As you can see, the delete operation returns the number of deleted records, in this case - 1.
Uncle Bob decided that Gerba had so many animals and squeezed Fido out of him:
>>> herb_fido.owner = uncle_bob
>>> herb_fido.save()
>>> bob_fido = herb_fido # переименуем переменную для лучшего соответствия суровой реальности
Selections
The selections are made directly with the class object and return SelectQuery instances (an analogue of QuerySet in the jung).
Retrieve a single record
To retrieve a single record, use the method
SelectQuery.get()
:>>> grandma = Person.select().where(Person.name == 'Grandma L.').get()
The query can be shortened by substituting the argument directly in
get()
:>>> grandma = Person.get(Person.name == 'Grandma L.')
Retrieving Multiple Records
Let's go through all the instances with a
Person
cycle:>>> for person in Person.select():
... print person.name, person.is_relative
...
Bob True
Grandma L. True
Herb False
Let's go through the instances
Person
and all the records associated with them:>>> 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
We catch all cats and their owners (or vice versa?):
>>> for pet in Pet.select().where(Pet.animal_type == 'cat'):
... print pet.name, pet.owner.name
...
Kitty Bob
Mittens Jr Herb
Not without join'ov:
# выберем всех животных Боба
>>> for pet in Pet.select().join(Person).where(Person.name == 'Bob'):
... print pet.name
...
Kitty
Fido
The same selection can be extracted in a different way - explicitly passing the object with Bob to the request:
>>> for pet in Pet.select().where(Pet.owner == uncle_bob):
... print pet.name
Sort the selection in alphabetical order. To do this, we use the method
SelectQuery.order_by()
:>>> for pet in Pet.select().where(Pet.owner == uncle_bob).order_by(Pet.name):
... print pet.name
...
Fido
Kitty
Sort people by age:
>>> for person in Person.select().order_by(Person.birthday.desc()):
... print person.name
...
Bob
Herb
Grandma L.
Let's try a more complex query. Choose all people born
- until 1940
- after 1959
>>> d1940 = date(1940, 1, 1)
>>> d1960 = date(1960, 1, 1)
>>> for person in Person.select().where((Person.birthday < d1940) | (Person.birthday > d1960)):
... print person.name
...
Bob
Grandma L.
Hint
The request
where((Person.birthday < d1940) | (Person.birthday > d1960))
can be written and how where(Person.birthday < d1940 or Person.birthday > d1960)
, but it is better not to do this, because peewee does not always correctly handle such a record.And now the toroboan. Choose those born between 1940 and 1960:
>>> for person in Person.select().where((Person.birthday > d1940) & (Person.birthday < d1960)):
... print person.name
...
Herb
And one last thing. We will use the SQL function and select all the people whose name begins with "G" in any register:
>>> for person in Person.select().where(fn.Lower(fn.Substr(Person.name, 1, 1)) == 'g'):
... print person.name
...
Grandma L.
For selections, also use methods:
SelectQuery.group_by()
SelectQuery.having()
SelectQuery.limit()
andSelectQuery.offset()
If you liked this short tutorial, be sure to visit the official documentation - there are a lot of interesting things, including recipes with solutions to common problems and a set of plugins that extend the basic functionality.
Bonus
The authors on his blog were asked about the speed of ORM, to which he replied:
On my machine peewee has been faster than Django and SQA at most tasks, and about the same when iterating and returning Model instances.
On my computer, peewee outperformed Django and SQLAlchemy on most tasks, and showed comparable results on iterations and sample instances.
Then he published the results of the benchmark on the github. We tested common models and related via ForeignKey in various scenarios. Very curious .
Who cares, the source:
- Django: models and benchmark
- SQLAlchemy: Models and Benchmark
- Peewee: models and benchmark
- Composite bench
A good alternative to Alchemy, what do you think?