Jac*_* M. 49 django django-models django-queryset django-managers
我正试图找到一种方法来实现自定义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()
Run Code Online (Sandbox Code Playgroud)
这很好,直到我做这样的事情:
inquiries = Inquiry.objects.filter(status=some_status)
my_inquiry_count = inquiries.for_user(request.user).count()
Run Code Online (Sandbox Code Playgroud)
这会立即打破一切,因为QuerySet它没有相同的方法Manager.我已经尝试创建一个自定义QuerySet类,并在其中实现它MyInquiryManager,但我最终复制了所有的方法定义.
我也发现这个片段有效,但我需要传递额外的参数,for_user因此它会因为重新定义而严重依赖get_query_set.
有没有办法在不重新定义子类QuerySet和Manager子类中的所有方法的情况下执行此操作?
T. *_*one 50
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)
Run Code Online (Sandbox Code Playgroud)
一旦你有了它,在你的模型上你需要做的就是定义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)
Run Code Online (Sandbox Code Playgroud)
使用此模式,其中任何一个都可以工作:
>>> Inquiry.objects.active_for_account(user)
>>> Inquiry.objects.all().active_for_account(user)
>>> Inquiry.objects.filter(first_name='John').active_for_account(user)
Run Code Online (Sandbox Code Playgroud)
UPD如果您正在使用自定义的用户(使用它AbstractUser),你需要改变
从
class CustomQuerySetManager(models.Manager):
Run Code Online (Sandbox Code Playgroud)
至
from django.contrib.auth.models import UserManager
class CustomQuerySetManager(UserManager):
***
Run Code Online (Sandbox Code Playgroud)
iMo*_*om0 32
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()
Run Code Online (Sandbox Code Playgroud)
有关详细信息,请参阅使用QuerySet方法创建管理器.
vdb*_*oor 10
您可以使用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)
Run Code Online (Sandbox Code Playgroud)
您现在可以在您的管理器上使用from_queryset()方法来更改其基本查询集。
这允许您只定义一次 Queryset 方法和管理器方法
从文档
对于高级用法,您可能需要自定义 Manager 和自定义 QuerySet。您可以通过调用 Manager.from_queryset() 来执行此操作,该方法返回带有自定义 QuerySet 方法副本的基本 Manager 的子类:
class InqueryQueryset(models.Queryset):
def custom_method(self):
""" available on all default querysets"""
class BaseMyInquiryManager(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())
)
MyInquiryManager = BaseInquiryManager.from_queryset(InquiryQueryset)
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()
Run Code Online (Sandbox Code Playgroud)
根据django 3.1.3源代码,我找到了一个简单的解决方案
from django.db.models.manager import BaseManager
class MyQuerySet(models.query.QuerySet):
def my_custom_query(self):
return self.filter(...)
class MyManager(BaseManager.from_queryset(MyQuerySet)):
...
class MyModel(models.Model):
objects = MyManager()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
27709 次 |
| 最近记录: |