Как использовать собственные менеджеры в цепочечных запросах? - PullRequest
13 голосов
/ 18 сентября 2011

Я создал собственный менеджер, который должен рандомизировать мой запрос:

class RandomManager(models.Manager):

    def randomize(self):        
        count = self.aggregate(count=Count('id'))['count']
        random_index = random.randint(0, count - 1)
        return self.all()[random_index]

Когда я вначале использую метод, определенный в моем менеджере, он работает нормально:

>>> PostPages.random_objects.randomize()
>>> <PostPages: post 3>

Мне нужно рандомизировать уже отфильтрованный запрос.Когда я попытался использовать менеджер и метод в цепочке, я получил ошибку:

PostPages.random_objects.filter(image_gallary__isnull=False).randomize()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/home/i159/workspace/shivaroot/shivablog/<ipython-input-9-98f654c77896> in <module>()
----> 1 PostPages.random_objects.filter(image_gallary__isnull=False).randomize()

AttributeError: 'QuerySet' object has no attribute 'randomize'

Результат фильтрации не является экземпляром класса модели, но это django.db.models.query.QuerySet, так что у него нет моегоменеджер и метод, соответственно. Есть ли способ использовать собственный менеджер в цепочке запросов?

Ответы [ 5 ]

27 голосов
/ 17 ноября 2012

Вот как вы объединяете пользовательские методы в пользовательский менеджер, например: Post.objects.by_author (user = request.user) .published ()

from django.db.models.query import QuerySet

class PostMixin(object):
    def by_author(self, user):
        return self.filter(user=user)

    def published(self):
        return self.filter(published__lte=datetime.now())

class PostQuerySet(QuerySet, PostMixin):
    pass

class PostManager(models.Manager, PostMixin):
    def get_query_set(self):
        return PostQuerySet(self.model, using=self._db)

Ссылка здесь: django-custom-модель-менеджер-цепочка

Примечание:

В Django 1.7 вы получаете это из коробки.Проверьте QuerySet.as_manager

12 голосов
/ 18 сентября 2011

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

10 голосов
/ 07 августа 2015

Просто пример кода с использованием нового метода as_manager () (см. Информацию об обновлении от @ zzart.

class MyQuerySet(models.query.QuerySet):
    def randomize(self):        
        count = self.aggregate(count=Count('id'))['count']
        random_index = random.randint(0, count - 1)
        return self.all()[random_index]

class MyModel(models.Model):
    .....
    .....
    objects = MyQuerySet.as_manager()
    .....
    .....

И тогда вы сможете использовать что-то подобное в своем коде:

MyModel.objects.filter(age__gt=16).randomize()

Как видите, новый as_manager () действительно аккуратен:)

0 голосов
/ 30 марта 2016

Учитывая, что у вас есть models.Manager, и вы не хотите предоставлять какой-либо метод менеджера для цепочки запросов, вы можете использовать Manager.from_queryset(QuerySet)().

Таким образом, вы по-прежнему можете размещать все свои цепочки методов набора запросов внутри QuerySet и метода менеджера независимо друг от друга.

Пример приведен на официальном сайте.

Фрагмент из Django Docs

class BaseManager(models.Manager):
    # Available only on Manager.
    def manager_only_method(self):
        return

class CustomQuerySet(models.QuerySet):
    # Available on both Manager and QuerySet.
    def manager_and_queryset_method(self):
        return

    # Available only on QuerySet.
    def _private_method(self):
        return

CustomManager = BaseManager.from_queryset(CustomQuerySet)

class MyModel(models.Model):
    objects = CustomManager()
0 голосов
/ 18 июня 2013

Как насчет чего-то подобного ниже, который динамически создает пользовательский QuerySet и позволяет нам «трансплантировать» наши пользовательские запросы в возвращенный экземпляр QuerySet:

class OfferManager(models.Manager):
    """
    Additional methods / constants to Offer's objects manager
    """
    ### Model (db table) wide constants - we put these and 
    ### not in model definition to avoid circular imports.
    ### One can access these constants through like
    <foo>.objects.STATUS_DISABLED or ImageManager.STATUS_DISABLED

    STATUS_DISABLED = 0
    ...
    STATUS_CHOICES = (
        (STATUS_DISABLED, "Disabled"),
        (STATUS_ENABLED, "Enabled"),
        (STATUS_NEGOTIATED, "Negotiated"),
        (STATUS_ARCHIVED, "Archived"),
    )
    ...

    # we keep status and filters naming a little different as
    # it is not one-to-one mapping in all situations
    QUERYSET_PUBLIC_KWARGS = {'status__gte': STATUS_ENABLED}
    QUERYSET_ACTIVE_KWARGS = {'status': STATUS_ENABLED}

    def get_query_set(self):
        """ our customized method which transpalats manager methods
        as per get_query_set.<method_name> = <method> definitions """
        CustomizedQuerySet = QuerySet
        for name, function in self.get_query_set.__dict__.items():
            setattr(CustomizedQuerySet, name, function)
        return CustomizedQuerySet(self.model, using=self._db)

    def public(self):
        """ Returns all entries accessible through front end site"""
        return self.all().filter(**OfferManager.QUERYSET_PUBLIC_KWARGS)
    get_query_set.public = public # will tranplat the function onto the 
                                  # returned QuerySet instance which 
                                  # means 'self' changes depending on context.

    def active(self):
        """ returns offers that are open to negotiation """
        return self.public().filter(**OfferManager.QUERYSET_ACTIVE_KWARGS)
    get_query_set.active = active
    ...

Более полированная версия этого метода и тикет Django здесь: https://code.djangoproject.com/ticket/20625.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...