Пользовательский QuerySet и менеджер, не повреждая DRY?

Я пытаюсь найти способ реализовать обоих пользовательское QuerySet и пользовательское Manager не повреждая DRY. Это - то, что я имею до сих пор:

class MyInquiryManager(models.Manager):
    def for_user(self, user):
        return self.get_query_set().filter(
                    Q(assigned_to_user=user) |
                    Q(assigned_to_group__in=user.groups.all())
                )

class Inquiry(models.Model):   
    ts = models.DateTimeField(auto_now_add=True)
    status = models.ForeignKey(InquiryStatus)
    assigned_to_user = models.ForeignKey(User, blank=True, null=True)
    assigned_to_group = models.ForeignKey(Group, blank=True, null=True)
    objects = MyInquiryManager()

Это хорошо работает, пока я не делаю что-то вроде этого:

inquiries = Inquiry.objects.filter(status=some_status)
my_inquiry_count = inquiries.for_user(request.user).count()

Это быстро повреждает все потому что QuerySet не имеет тех же методов как Manager. Я попытался создать пользовательское QuerySet класс и реализация его в MyInquiryManager, но я заканчиваю тем, что копировал все свои определения метода.

Я также нашел этот отрывок, который работает, но я должен передать в дополнительном аргументе for_user таким образом, это ломается, потому что это полагается в большой степени на переопределение get_query_set.

Есть ли способ сделать это, не переопределяя все мои методы в обоих QuerySet и Manager подклассы?

49
задан Jack M. 29 January 2010 в 06:00
поделиться

2 ответа

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


Я реализовал это путем добавления фактического get_active_for_account в качестве метода пользовательского QuerySet . Затем, чтобы заставить его работать вне менеджера, вы можете просто перехватить __ getattr __ и вернуть его соответствующим образом

Чтобы сделать этот шаблон повторно используемым, я извлек Manager биты в отдельный менеджер модели:

custom_queryset / models.py

from django.db import models
from django.db.models.query import QuerySet

class CustomQuerySetManager(models.Manager):
    """A re-usable Manager to access a custom QuerySet"""
    def __getattr__(self, attr, *args):
        try:
            return getattr(self.__class__, attr, *args)
        except AttributeError:
            # don't delegate internal methods to the queryset
            if attr.startswith('__') and attr.endswith('__'):
                raise
            return getattr(self.get_query_set(), attr, *args)

    def get_query_set(self):
        return self.model.QuerySet(self.model, using=self._db)

Как только вы это получите, на ваших моделях все, что вам нужно сделать, это определить QuerySet как настраиваемый внутренний класс и установить от менеджера к вашему пользовательскому менеджеру:

your_app / models.py

from custom_queryset.models import CustomQuerySetManager
from django.db.models.query import QuerySet

class Inquiry(models.Model):
    objects = CustomQuerySetManager()

    class QuerySet(QuerySet):
        def active_for_account(self, account, *args, **kwargs):
            return self.filter(account=account, deleted=False, *args, **kwargs)

С этим шаблоном любой из них будет работать:

>>> Inquiry.objects.active_for_account(user)
>>> Inquiry.objects.all().active_for_account(user)
>>> Inquiry.objects.filter(first_name='John').active_for_account(user)

UPD, если вы используете его с другим пользователем ( AbstractUser ), вам нужно изменить
с

class CustomQuerySetManager(models.Manager):

на

from django.contrib.auth.models import UserManager

class CustomQuerySetManager(UserManager):
    ***
50
ответ дан 7 November 2019 в 11:40
поделиться

Для меня следующие работы.

def get_active_for_account(self,account,*args,**kwargs):
    """Returns a queryset that is 
    Not deleted
    For the specified account
    """
    return self.filter(account = account,deleted=False,*args,**kwargs)

Это на диспетчере по умолчанию; Поэтому я делал что-то вроде:

Model.objects.get_active_for_account(account).filter()

, но нет причин, по которой он не должен работать для вторичного менеджера.

-1
ответ дан 7 November 2019 в 11:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: