如何在链查询中使用自定义管理器?
Posted
技术标签:
【中文标题】如何在链查询中使用自定义管理器?【英文标题】:How to use custom managers in chain queries? 【发布时间】:2011-11-19 15:15:02 【问题描述】:我创建了一个自定义管理器,它必须随机化我的查询:
class RandomManager(models.Manager):
def randomize(self):
count = self.aggregate(count=Count('id'))['count']
random_index = random.randint(0, count - 1)
return self.all()[random_index]
当我首先使用经理中定义的方法时,它可以正常工作:
>>> PostPages.random_objects.randomize()
>>> <PostPages: post 3>
我需要随机化已经过滤的查询。当我尝试使用管理器和链中的方法时出现错误:
PostPages.random_objects.filter(image_gallary__isnull=False).randomize()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/home/i159/workspace/shivaroot/shivablog/<ipython-input-9-98f654c77896> in <module>()
----> 1 PostPages.random_objects.filter(image_gallary__isnull=False).randomize()
AttributeError: 'QuerySet' object has no attribute 'randomize'
过滤的结果不是模型类的实例,而是django.db.models.query.QuerySet
,所以分别没有我的管理器和方法。
有没有办法在链式查询中使用自定义管理器?
【问题讨论】:
【参考方案1】:鉴于您有一个现有的models.Manager
,并且您不想将某些管理器方法公开给可链接的查询集,您可以使用Manager.from_queryset(QuerySet)()
。
因此,您仍然可以将所有可链接的查询集方法分别放在 QuerySet 和管理器方法中。
官网给出的例子。
来自 Django 文档的片段
class BaseManager(models.Manager):
# Available only on Manager.
def manager_only_method(self):
return
class CustomQuerySet(models.QuerySet):
# Available on both Manager and QuerySet.
def manager_and_queryset_method(self):
return
# Available only on QuerySet.
def _private_method(self):
return
CustomManager = BaseManager.from_queryset(CustomQuerySet)
class MyModel(models.Model):
objects = CustomManager()
【讨论】:
【参考方案2】:只是一个使用新 as_manager() 方法的代码示例(请参阅来自@zzart 的更新信息。
class MyQuerySet(models.query.QuerySet):
def randomize(self):
count = self.aggregate(count=Count('id'))['count']
random_index = random.randint(0, count - 1)
return self.all()[random_index]
class MyModel(models.Model):
.....
.....
objects = MyQuerySet.as_manager()
.....
.....
然后你就可以在你的代码中使用这样的东西了:
MyModel.objects.filter(age__gt=16).randomize()
如你所见,新的 as_manager() 非常简洁:)
【讨论】:
刚刚使用.as_manager()
并验证这是这样做的方法。谢谢。【参考方案3】:
这是您在自定义管理器上链接自定义方法的方式,即:Post.objects.by_author(user=request.user).published()
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)
链接在这里:django-custom-model-manager-chaining
注意:
在 Django 1.7 中,您可以立即使用它。查看QuerySet.as_manager
【讨论】:
这真是太好了。您可以在Manager
和 Queryset
中访问所有自定义方法,并且它们都定义在一个类中。为了进一步推动它,可以定义装饰器以使代码库更小。虽然,我不知道如何实现它。
非常好的方法
+1 表示 Django 1.7 的方式,但不应该是 QuerySet.as_manager() 吗?在我看来,这也应该被标记为正确答案..
指定继承时mixin类不应该在基类的左边吗?【参考方案4】:
如何动态创建自定义 QuerySet 并允许我们将自定义查询“移植”到返回的 QuerySet 实例上:
class OfferManager(models.Manager):
"""
Additional methods / constants to Offer's objects manager
"""
### Model (db table) wide constants - we put these and
### not in model definition to avoid circular imports.
### One can access these constants through like
<foo>.objects.STATUS_DISABLED or ImageManager.STATUS_DISABLED
STATUS_DISABLED = 0
...
STATUS_CHOICES = (
(STATUS_DISABLED, "Disabled"),
(STATUS_ENABLED, "Enabled"),
(STATUS_NEGOTIATED, "Negotiated"),
(STATUS_ARCHIVED, "Archived"),
)
...
# we keep status and filters naming a little different as
# it is not one-to-one mapping in all situations
QUERYSET_PUBLIC_KWARGS = 'status__gte': STATUS_ENABLED
QUERYSET_ACTIVE_KWARGS = 'status': STATUS_ENABLED
def get_query_set(self):
""" our customized method which transpalats manager methods
as per get_query_set.<method_name> = <method> definitions """
CustomizedQuerySet = QuerySet
for name, function in self.get_query_set.__dict__.items():
setattr(CustomizedQuerySet, name, function)
return CustomizedQuerySet(self.model, using=self._db)
def public(self):
""" Returns all entries accessible through front end site"""
return self.all().filter(**OfferManager.QUERYSET_PUBLIC_KWARGS)
get_query_set.public = public # will tranplat the function onto the
# returned QuerySet instance which
# means 'self' changes depending on context.
def active(self):
""" returns offers that are open to negotiation """
return self.public().filter(**OfferManager.QUERYSET_ACTIVE_KWARGS)
get_query_set.active = active
...
此方法的更完善版本和 django 票在这里:https://code.djangoproject.com/ticket/20625。
【讨论】:
【参考方案5】:看起来这个 sn-p 为您的情况提供了解决方案:Custom managers with chainable filters。
【讨论】:
如果您使用的是 Django 1.7+,请参阅下面的答案,因为该功能是开箱即用的。以上是关于如何在链查询中使用自定义管理器?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Windows 身份验证并注册自定义身份验证管理器