Peewee - lightweight, flexible and very fast ORM in Python

  • Tutorial
image

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:
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:

OptionDescriptionInherited?
databasemodel databaseYes
db_tablename of the table in which data will be storednot
indexeslist of fields to indexYes
order_bylist of fields to sort by defaultYes
primary_keycomposite primary key, an instance of the CompositeKey class, exampleYes
table_aliasalias tables for use in queriesnot


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 Personcycle:

>>> for person in Person.select():
...     print person.name, person.is_relative
...
Bob True
Grandma L. True
Herb False


Let's go through the instances Personand 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() and SelectQuery.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:


A good alternative to Alchemy, what do you think?

Also popular now: