- PVSM.RU - https://www.pvsm.ru -

Внедрение высокопроизводительного Pony ORM в проект Django

Предыдущий пост [1] посвященный производительности, описывал Pony ORM [2], показавший фантастические результаты по сравнению с Django ORM и SQLAlchemy.

Впечатленный столь неординарными результатами и озабоченный производительностью собственного проекта, я решил внедрить Pony ORM в свой проект. Что из этого получилось, см подкатом.

Соблазны

Конечно, можно было бы переписать проект заново. Этот соблазн всегда витает над разработчиком, встретившим проблемы, корни которых лежат в используемом инструменте. Однако, объем уже написанного кода, огромная разветвленная модель данных, да и впечатляющее количество использованных в проекте вполне рабочих плагинов-приложений для Django ставят на этом пути большой и жирный крест.

Альтернатива

Некоторое время назад, в поисках альтернативы Django ORM, я наткнулся на интересный проект Aldjemy [3]. Это небольшая навеска над Django ORM, позволяющая использовать структуру моделей Django для построения альтернативной иерархии моделей SQLAlchemy. Такой подход дает возможность, сохраняя основу проекта (в том числе — всю модель данных Django), использовать SQLAlchemy именно и только там, где вы захотите. Вдохновленный идеей этого проекта и слизав некоторое количество строк кода, я сделал аналогичную библиотеку [4] для прикрутки Pony ORM к Django ORM, назвав ее djony (DJango pONY).

Краткий экскурс

Использование djony более чем просто. После установки djony в систему (например с помощью команды pip install git+git://github.com/nnseva/djony.git@master#egg=djony), мы можем указать djony в качестве одного из используемых приложений в settings.py. Нужно только помнить, что djony обязательно должен быть самым последним приложением в списке.

Теперь у каждой из моделей Django (именно моделей — как классов) появился атрибут `p` (от слова pony). Это и есть модель Pony ORM. Ее можно использовать везде, где согласно документации [5] Pony, нужно использовать модель. Вам понадобится также модуль orm, его можно импортировать из модуля pony (from pony import orm). Альтернативно, вы можете использовать модуль djony.orm, содержащий все переменные модуля pony.orm, а также специфические для djony функции и переменные.

Объекты моделей Pony ORM будут содержать только поля данных и коллекции объектов согласно модели данных и структуре отношений (возможно, в будущем надо будет прикрутить возможность добавления своих членов в автоматически создаваемые модели Pony ORM).

Тест

Попробуем теперь потестировать на производительность одну из самых популярных операций — проверку прав пользователя.

Для Django мы будем использовать готовую функцию User.has_perm. Разумеется, для Pony ORM нужно будет написать код, примерно эквивалентный этой функции::

def has_perm(user,perm):
    if not user.is_active:
        return False
    app_label,codename = perm.split('.')
    for p in orm.select(
            p for p in Permission.p
            if
                (user in p.user_set or user in p.group_set.user_set) and
                p.codename == codename and
                p.content_type.app_label == app_label
    ):
        return True
    return False

Посмотрим на результаты тестирования (для тестирования, в базе было заведено 1000 пользователей, выполняющих для теста роль балласта).

>>> import test_pony
>>> import test_django
>>> test_django.test_django()
check user permissions: django req/seq: 170.308759221 req time (ms): 5.8716886
>>> test_pony.test_pony()
check user permissions: pony req/seq: 729.517146462 req time (ms): 1.3707697

Как видим, мы получили прирост более чем в 4 раза. Очень неплохо!

Приложение. Код тестов.

test_django.py
import datetime

from django.contrib.auth.models import User

def test_django():
    t1 = datetime.datetime.now()
    for i in range(10000):
        test()
    t2 = datetime.datetime.now()
    print "check user permissions: django req/seq:",10000/(t2-t1).total_seconds(),'req time (ms):',(t2-t1).total_seconds()/10.

def test():
    user = User.objects.get(username='testuser')
    return user.has_perm('auth.add_user')
test_pony.py
import datetime

from django.contrib.auth.models import User, Permission

def test_pony():
    t1 = datetime.datetime.now()
    for i in range(10000):
        test()
    t2 = datetime.datetime.now()
    print "check user permissions: pony req/seq:",10000/(t2-t1).total_seconds(),'req time (ms):',(t2-t1).total_seconds()/10.

from djony import orm

@orm.db_session
def test():
    user = User.p.get(username='testuser')
    return has_perm(user,'auth.add_user')

def has_perm(user,perm):
    if not user.is_active:
        return False
    app_label,codename = perm.split('.')
    for p in orm.select(
            p for p in Permission.p
            if
                (user in p.user_set or user in p.group_set.user_set) and
                p.codename == codename and
                p.content_type.app_label == app_label
    ):
        return True
    return False

Автор: nnseva

Источник [6]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/45924

Ссылки в тексте:

[1] пост: http://habrahabr.ru/post/188842/

[2] Pony ORM: http://www.ponyorm.com/

[3] Aldjemy: https://github.com/Deepwalker/aldjemy

[4] аналогичную библиотеку: https://github.com/nnseva/djony

[5] согласно документации: http://doc.ponyorm.com/

[6] Источник: http://habrahabr.ru/post/197962/