Я пытаюсь найти способ реализовать обоих пользовательское 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
подклассы?
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):
***
Для меня следующие работы.
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()
, но нет причин, по которой он не должен работать для вторичного менеджера.