自定义查询集和管理器没有中断DRY? [英] Custom QuerySet and Manager without breaking DRY?

查看:226
本文介绍了自定义查询集和管理器没有中断DRY?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图找到一种实现自定义 QuerySet 和自定义管理器而不中断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())


类查询(models.Model):
ts = models.DateTimeField(auto_now_add = True)
status = models.ForeignKey(InquiryStatus)
assigned_to_user = models.ForeignKey ,blank = True,null = True)
assigned_to_group = models.ForeignKey(Group,blank = True,null = True)
objects = MyInquiryManager()
/ pre>

这样做不错,直到我做这样的事情:

  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



有没有办法做到这一点,没有重新定义我的所有方法在$ $ $ c> QuerySet 和管理器子类?

解决方案

Django已更改!在使用2009年写的答案之前,请务必查看其余的答案和Django文档,看看是否有更合适的解决方案。






我实现这个的方法是添加实际 get_active_for_account 作为自定义 QuerySet 的方法。然后,为了使经理工作,你可以简单地陷入 __ getattr __ 并相应地返回



这种模式可重用,我已经将管理器位提取到单独的模型管理器中:



django.db导入模型
从django.db.models
$ b $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ .query import QuerySet

class CustomQuerySetManager(models.Manager):
可重用的Manager访问自定义QuerySet
def __getattr __(self,attr ,* args):
try:
return getattr(self .__ class__,attr,* args)
除了AttributeError:
#不要将内部方法委派给queryset
如果attr.startswith('__')和attr.endswith('__'):
raise
return getattr(self.get_query_set(),attr,* args)

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

一旦你有了,在你的您需要做的所有模型是将 QuerySet 定义为自定义内部类,并将管理器设置为自定义管理器:



your_app / models.py

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

类查询(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)


I'm trying to find a way to implement both a custom QuerySet and a custom Manager without breaking DRY. This is what I have so far:

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

This works fine, until I do something like this:

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

This promptly breaks everything because the QuerySet doesn't have the same methods as the Manager. I've tried creating a custom QuerySet class, and implementing it in MyInquiryManager, but I end up replicating all of my method definitions.

I also found this snippet which works, but I need to pass in the extra argument to for_user so it breaks down because it relies heavily on redefining get_query_set.

Is there a way to do this without redefining all of my methods in both the QuerySet and the Manager subclasses?

解决方案

Django has changed! Before using the code in this answer, which was written in 2009, be sure to check out the rest of the answers and the Django documentation to see if there is a more appropriate solution.


The way I've implemented this is by adding the actual get_active_for_account as a method of a custom QuerySet. Then, to make it work off the manager, you can simply trap the __getattr__ and return it accordingly

To make this pattern re-usable, I've extracted out the Manager bits to a separate model 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)

Once you've got that, on your models all you need to do is define a QuerySet as a custom inner class and set the manager to your custom manager:

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)

With this pattern, any of these will work:

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

这篇关于自定义查询集和管理器没有中断DRY?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆