24 августа 2012 г. Django Python

Django. Кастомный QuerySet и Manager для добавления в цепь вызова дополнительных методов

Иногда необходимо добавить в цепь вызова кастомные методы для QuerySet, но если определить просто свой менеджер модели, то этого оказывается недостаточно, потому что он действует только как точка доступа к QuerySet.


Пример использования менеджера модели без цепи вызовов:

from django.db import models


class CustomManager(models.Manager):
    def custom_filter(self, **kwargs):
        kwargs['status'] = 'published'
        return super(CustomManager, self).get_query_set().filter(**kwargs)

    def custom_order_by(self, *args):
        args = ('published', ) + args
        return super(CustomManager, self).get_query_set().order_by(*args)


class MyModel(models.Model):
    ...
    custom_manager = CustomManager()

Далее вы вызываете его так:

MyModel.custom_manager.custom_filter(something=42)
# или так
MyModel.custom_manager.custom_order_by('some_field')

Но если вы хотите использовать в цепи вызовов, то ничего не выйдет, так как в вашем "custom_filter" вы возвращаете models.query.QuerySet и связь с менеджером на этом заканчивается:

# Так уже сделать не получится:
MyModel.custom_manager.custom_filter(something=42).custom_order_by('some_field')

Чтобы исправить эту ситуацию необходимо определить свой QuerySet и в нём уже определять свои методы, а не в менеджере. А в самом менеджере только определить "get_query_set", который будет возвращать методы вашего CustomQuerySet:

from django.db import models


class CustomQuerySet(models.query.QuerySet):
    """Substitution the QuerySet, and adding additional methods to QuerySet
    """

    def custom_filter(self, **kwargs):
        kwargs['status'] = "published"
        return self.filter(**kwargs)

    def custom_order_by(self, *args):
        args = ('published', ) + args
        return self.order_by(*args)


class CustomManager(models.Manager):
    def get_query_set(self):
        model = models.get_model(self.model._meta.app_label, self.model._meta.object_name)
        return CustomQuerySet(model)

    def __getattr__(self, attr, *args):
        try:
            return getattr(self.__class__, attr, *args)
        except AttributeError:
            return getattr(self.get_query_set(), attr, *args)

В коде проекта просто используйте CustomManager или наследуйте свои менеджеры от него, чтобы добавить ваши дополнительные методы из CustomQuerySet:

class MyModel(models.Model):
    ...
    custom_manager = CustomManager()

MyModel.custom_manager.custom_filter(something=42).custom_order_by('some_field')

Так, например, у меня сделан поиск в Marcus. Правда сам поиск пока временный (через "LIKE"), потом хочу сделать возможность передавать свои методы поиска, например "MATCH/AGAINST" или "SphinxSearch".

По мотивам http://djangosnippets.org/snippets/562/

38177714.9958222.1347833143.c8f1dc4a1b410c2ad13b0ef9e4f3d2f4

Комментарии

Стоит взглянуть на это решение https://bitbucket.org/carljm/django-model-utils/src

Оставьте свой комментарий

Markdown