我正试图找到一种方法来实现自定义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): ***
Django 1.7发布了一种创建组合查询集和模型管理器的新方法:
class InquiryQuerySet(models.QuerySet): def for_user(self): return self.filter( Q(assigned_to_user=user) | Q(assigned_to_group__in=user.groups.all()) ) class Inquiry(models.Model): objects = InqueryQuerySet.as_manager()
有关详细信息,请参阅使用QuerySet方法创建管理器.
您可以使用mixin在manager和queryset上提供方法.请参阅以下技术:
http://hunterford.me/django-custom-model-manager-chaining/
这也避免了使用__getattr__()
方法.
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)