- PVSM.RU - https://www.pvsm.ru -
Предлагаю всем джангистам/алхимистам немного отвечься и почитать вольную интерпретацию вводного туториала и частично документации по Peewee – stand-alone ORM, обязательной к ознакомлению любому питонщику и, в особенности, фласкеру. Пишут о ней мало, а зря. С Peewee очень просто подружиться, особенно если вы уже знакомы с какой-нибудь ORM на ActiveRecord. Что более важно – с ней приятно дружить :) Ну, начнём.
pip install peewee
Из репозитория:
git clone https://github.com/coleifer/peewee.git cd peewee python setup.py install
Тесты:
python setup.py test
Есть обвязка для flask:
pip install flask-peewee
Весь нижеследующий код можно повторить один к одному в интерактивном интерпретаторе или отдельном скрипте.
from peewee import *
db = SqliteDatabase('people.db')
class Person(Model):
name = CharField()
birthday = DateField()
is_relative = BooleanField()
class Meta:
database = db # модель будет использовать базу данных 'people.db'
Типов полей много, на все случаи жизни [1]. Peewee берёт на себя преобразование питоновских объектов в значения, подходящие для базы данных, и наоборот.
Каждое поле принимает следующие инициализирующие аргументы:
null=False
– возможно ли хранение null-значений;index=False
– создавать ли индекс для данного столбца в базе;unique=False
– создавать ли уникальный индекс для данного столбца в базе. См. также главу о составных индексах [2];verbose_name=None
– строка для человекопонятного представления поля;help_text=None
– строка с вспомогательным текстом для поля;db_column=None
– строка, явно задающая название столбца в базе для данного поля, используется например при работе с legacy базой данных;default=None
– значение по-умолчанию для полей класса при инстанцировании;choices=None
– список или кортеж двухэлементных кортежей, где первый элемент – значение для базы, второй – отображаемое значение (аналогично джанге);primary_key=False
– использовать ли данное поле, как первичный ключ;sequence=None
– последовательность для наполнения поля (удостоверьтесь, что бекэнд поддерживает такую функциональность);Для каждой таблицы можно прописать единые метаданные в class Meta
:
Опция | Описание | Наследуется? |
---|---|---|
database |
база данных для модели | да |
db_table |
название таблицы, в которой будут храниться данные | нет |
indexes |
список полей для индексирования | да |
order_by |
список полей для сортировки по-умолчанию | да |
primary_key |
составной первичный ключ, экземпляр класса CompositeKey, пример [3] | да |
table_alias |
алиас таблицы для использования в запросах | нет |
Попробуем задать отношения между моделями через внешний ключ. С peewee это просто:
class Pet(Model):
owner = ForeignKeyField(Person, related_name='pets')
name = CharField()
animal_type = CharField()
class Meta:
database = db # модель будет использовать базу данных 'people.db'
Модели описаны, осталось создать для них соответствующие таблицы в базе данных:
>>> Person.create_table()
>>> Pet.create_table()
Для примера создадим нескольких человек и заведём им домашних животных:
>>> from datetime import date
>>> uncle_bob = Person(name='Bob', birthday=date(1960, 1, 15), is_relative=True)
>>> uncle_bob.save() # cохраним Боба в базе данных
Записи можно создавать и напрямую с помощью метода Model.create() без явного 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)
Порадуем бабулю фамилией:
>>> grandma.name = 'Grandma L.'
>>> grandma.save() # обновим запись grandma
Теперь сгенерируем немного живности. У бабули аллергия на кошек, а вот у Герба есть некоторые проблемы [4]:
>>> 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() # удачи, Варежка
1
Как вы могли заметить, операция удаления возвращает количество удалённых записей, в данном случае – 1.
Дядя Боб решил, что у Герба итак много животных и отжал у него Фидо:
>>> herb_fido.owner = uncle_bob
>>> herb_fido.save()
>>> bob_fido = herb_fido # переименуем переменную для лучшего соответствия суровой реальности
Выборки выполняются прямо с объектом класса и возвращают экземпляры SelectQuery (аналог QuerySet в джанге).
Для извлечения одной записи используйте метод SelectQuery.get()
:
>>> grandma = Person.select().where(Person.name == 'Grandma L.').get()
Запрос можно сократить, подставив аргумент напрямую в get()
:
>>> grandma = Person.get(Person.name == 'Grandma L.')
Пройдемся по всем экземплярам Person
циклом:
>>> for person in Person.select():
... print person.name, person.is_relative
...
Bob True
Grandma L. True
Herb False
Пройдемся по экземплярам Person
и по всем связанным с ними записями:
>>> 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
Выловим всех кошек и их хозяев людей (или наоборот?):
>>> for pet in Pet.select().where(Pet.animal_type == 'cat'):
... print pet.name, pet.owner.name
...
Kitty Bob
Mittens Jr Herb
Не без join'ов:
# выберем всех животных Боба
>>> for pet in Pet.select().join(Person).where(Person.name == 'Bob'):
... print pet.name
...
Kitty
Fido
Извлечь ту же выборку можно и по-другому – явно передав объект с Бобом в запрос:
>>> for pet in Pet.select().where(Pet.owner == uncle_bob):
... print pet.name
Упорядочим выборку в алфавитном порядке. Для этого воспользуемся методом SelectQuery.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
...
Bob
Herb
Grandma L.
Давайте попробуем более сложный запрос. Выберем всех людей, родившихся
>>> 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.
where((Person.birthday < d1940) | (Person.birthday > d1960))
можно написать и как where(Person.birthday < d1940 or Person.birthday > d1960)
, но лучше этого не делать, т.к. peewee не всегда правильно обрабатывает такую запись.А теперь торобоан. Выберем тех, кто родился между 1940 и 1960:
>>> for person in Person.select().where((Person.birthday > d1940) & (Person.birthday < d1960)):
... print person.name
...
Herb
And one last thing. Воспользуемся SQL-функцией и выберем всех людей, чьё имя начинается с «G» в любом регистре:
>>> for person in Person.select().where(fn.Lower(fn.Substr(Person.name, 1, 1)) == 'g'):
... print person.name
...
Grandma L.
Для выборок также используйте методы:
SelectQuery.group_by()
SelectQuery.having()
SelectQuery.limit()
и SelectQuery.offset()
Если вам понравился данный краткий туториал, обязательно посетите официальную документацию [5] — там много интересного, включая рецепты с решениями распространённых задач [6] и набор плагинов [7], расширяющих базовую функциональность.
Автора в его блоге спросили о быстродействии ORM, на что тот ответил:
On my machine peewee has been faster than Django and SQA at most tasks, and about the same when iterating and returning Model instances.
На моём компе peewee обошла Django и SQLAlchemy на большинстве задач, и показала сравнимые результаты на итерациях и выборке инстансов.
После чего опубликовал результаты бенчмарка на гитхабе. Тестились обычные модели и связанные через ForeignKey в различных сценариях. Весьма любопытно [8].
Кому интересно, исходники:
Хорошая альтернатива Алхимии, как считаете?
Автор: rzhannoy
Источник [16]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/51479
Ссылки в тексте:
[1] на все случаи жизни: http://peewee.readthedocs.org/en/latest/peewee/models.html#field-types-table
[2] о составных индексах: http://peewee.readthedocs.org/en/latest/peewee/models.html#model-indexes
[3] пример: http://peewee.readthedocs.org/en/latest/peewee/api.html#CompositeKey
[4] некоторые проблемы: http://ru.wikipedia.org/wiki/%D0%9F%D0%B0%D1%82%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BD%D0%B0%D0%BA%D0%BE%D0%BF%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D1%81%D1%82%D0%B2%D0%BE_%D0%B6%D0%B8%D0%B2%D0%BE%D1%82%D0%BD%D1%8B%D1%85
[5] официальную документацию: http://peewee.readthedocs.org/en/latest/
[6] рецепты с решениями распространённых задач: http://peewee.readthedocs.org/en/latest/peewee/cookbook.html
[7] набор плагинов: http://peewee.readthedocs.org/en/latest/peewee/playhouse.html
[8] любопытно: https://github.com/coleifer/peewee/blob/master/bench/results
[9] модели: https://github.com/coleifer/peewee/blob/master/bench/django_bench/models.py
[10] бенчмарк: https://github.com/coleifer/peewee/blob/master/bench/django_bench/bench.py
[11] модели: https://github.com/coleifer/peewee/blob/master/bench/sqlalc_bench/models.py
[12] бенчмарк: https://github.com/coleifer/peewee/blob/master/bench/sqlalc_bench/bench.py
[13] модели: https://github.com/coleifer/peewee/blob/master/bench/peewee_bench/models.py
[14] бенчмарк: https://github.com/coleifer/peewee/blob/master/bench/peewee_bench/bench.py
[15] Сводный бенч: https://github.com/coleifer/peewee/blob/master/bench/run_bench.py
[16] Источник: http://habrahabr.ru/post/207110/
Нажмите здесь для печати.