代码之家  ›  专栏  ›  技术社区  ›  Jack M.

自定义查询集和管理器而不破坏干燥?

  •  47
  • Jack M.  · 技术社区  · 14 年前

    我正试图找到一种方法来实现这两个自定义 QuerySet 还有一种习俗 Manager 不会干裂。这就是我目前为止所拥有的:

    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()
    

    这很快就打破了一切,因为 查询集 方法与 经理 . 我试过创建一个自定义 查询集 类,并在中实现它 MyInquiryManager 但是我最终复制了我所有的方法定义。

    我也发现了 this snippet 这是可行的,但我需要把额外的论点传给 for_user 所以它会崩溃,因为它严重依赖于重新定义 get_query_set .

    有没有一种方法可以做到这一点而不重新定义我的所有方法 查询集 以及 经理 子类?

    5 回复  |  直到 6 年前
        1
  •  48
  •   T. Stone    6 年前

    姜戈变了! 在使用2009年编写的此答案中的代码之前,请务必查看其余答案和Django文档,以查看是否有更合适的解决方案。


    我实现这一点的方法是添加 get_active_for_account 作为一种习惯的方法 QuerySet .然后,为了使它脱离经理,您可以简单地将 __getattr__ 并相应返还

    为了使这个模式可用,我提取了 Manager 位到单独的模型管理器:

    自定义查询集/模型.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)
    

    一旦你得到了它,在你的模型上你需要做的就是定义一个 查询集 作为自定义内部类,并将管理器设置为自定义管理器:

    您的应用程序/模型.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)
    

    如果与自定义用户一起使用,则更新( AbstractUser )你需要改变

    class CustomQuerySetManager(models.Manager):
    

    from django.contrib.auth.models import UserManager
    
    class CustomQuerySetManager(UserManager):
        ***
    
        2
  •  28
  •   rattray    8 年前

    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()
    

    Creating Manager with QuerySet methods 了解更多详细信息。

        3
  •  10
  •   vdboor    11 年前

    可以使用mixin在管理器和查询集上提供方法。请参见以下技术:

    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)
    
        4
  •  3
  •   Roman Odaisky    11 年前

    T.Stone_s方法的一个稍有改进的版本:

    def objects_extra(mixin_class):
        class MixinManager(models.Manager, mixin_class):
            class MixinQuerySet(QuerySet, mixin_class):
                pass
    
            def get_query_set(self):
                return self.MixinQuerySet(self.model, using=self._db)
    
        return MixinManager()
    

    类修饰符使使用简单如下:

    class SomeModel(models.Model):
        ...
        @objects_extra
        class objects:
            def filter_by_something_complex(self, whatever parameters):
                return self.extra(...)
            ...
    

    更新:支持非标准管理器和查询集基类,例如@objects-extra(django.contrib.gis.db.models.geomanager、django.contrib.gis.db.models.query.geoqueryset):

    def objects_extra(Manager=django.db.models.Manager, QuerySet=django.db.models.query.QuerySet):
        def oe_inner(Mixin, Manager=django.db.models.Manager, QuerySet=django.db.models.query.QuerySet):
            class MixinManager(Manager, Mixin):
                class MixinQuerySet(QuerySet, Mixin):
                    pass
    
                def get_query_set(self):
                    return self.MixinQuerySet(self.model, using=self._db)
    
            return MixinManager()
    
        if issubclass(Manager, django.db.models.Manager):
            return lambda Mixin: oe_inner(Mixin, Manager, QuerySet)
        else:
            return oe_inner(Mixin=Manager)
    
        5
  •  -1
  •   lprsd    14 年前

    以下是我的作品。

    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()
    

    但对于二级经理来说,这是没有理由不起作用的。