Django经理Chaining

Ada*_*dam 29 python django django-models django-managers

我想知道是否有可能(以及如果是这样,如何)将多个经理链接在一起以生成受两个经理影响的查询集.我将解释我正在研究的具体示例:

我有多个抽象模型类,用于为其他模型提供小的特定功能.其中两个模型是DeleteMixin和GlobalMixin.

DeleteMixin定义如下:

class DeleteMixin(models.Model):
    deleted = models.BooleanField(default=False)
    objects = DeleteManager()

    class Meta:
        abstract = True

    def delete(self):
        self.deleted = True
        self.save()
Run Code Online (Sandbox Code Playgroud)

基本上它提供了伪删除(删除标志)而不是实际删除对象.

GlobalMixin定义如下:

class GlobalMixin(models.Model):
    is_global = models.BooleanField(default=True)

    objects = GlobalManager()

    class Meta:
        abstract = True
Run Code Online (Sandbox Code Playgroud)

它允许将任何对象定义为全局对象或私有对象(例如公共/私有博客帖子).

这两个都有自己的管理器,会影响返回的查询集.我的DeleteManager过滤查询集只返回已删除标志设置为False的结果,而GlobalManager过滤查询集只返回标记为全局的结果.以下是两者的声明:

class DeleteManager(models.Manager):
    def get_query_set(self):
        return super(DeleteManager, self).get_query_set().filter(deleted=False)

class GlobalManager(models.Manager):
    def globals(self):
        return self.get_query_set().filter(is_global=1)
Run Code Online (Sandbox Code Playgroud)

所需的功能是让模型扩展这两个抽象模型,并授予仅返回非删除和全局结果的能力.我在一个包含4个实例的模型上运行了一个测试用例:一个是全局的,未删除的,一个是全局的,已删除,一个是非全局的,未删除,一个是非全局的,已删除.如果我尝试获取结果集:SomeModel.objects.all(),我得到实例1和3(两个未删除的 - 很棒!).如果我尝试SomeModel.objects.globals(),我会得到一个错误,即DeleteManager没有全局变量(假设我的模型声明是这样的:SomeModel(DeleteMixin,GlobalMixin).如果我颠倒顺序,我不会得到错误,但它不会过滤掉已删除的错误).如果我更改GlobalMixin以将GlobalManager连接到全局而不是对象(因此新命令将是SomeModel.globals.globals()),我得到实例1和2(两个全局变量),而我的预期结果只是获取实例1(全局,未删除的).

我不确定是否有人遇到过与此类似的任何情况并且已经得出结果.无论是在我当前的想法中使其工作的方式还是提供我所追求的功能的重新工作都将非常感激.我知道这篇文章有点啰嗦.如果需要更多解释,我很乐意提供.

编辑:

我已经在下面发布了我用于此特定问题的最终解决方案.它基于Simon自定义QuerySetManager的链接.

Sco*_*ott 21

请参阅Djangosnippets上的这个片段:http://djangosnippets.org/snippets/734/

您不必将自定义方法放在管理器中,而是将查询集本身子类化.它非常简单,工作完美.我唯一的问题是模型继承,你总是必须在模型子类中定义管理器(只是:子类中的"objects = QuerySetManager()"),即使它们将继承查询集.一旦使用QuerySetManager,这将更有意义.


Ada*_*dam 8

以下是使用Scott链接到的Simon自定义QuerySetManager的问题的具体解决方案.

from django.db import models
from django.contrib import admin
from django.db.models.query import QuerySet
from django.core.exceptions import FieldError

class MixinManager(models.Manager):    
    def get_query_set(self):
        try:
            return self.model.MixinQuerySet(self.model).filter(deleted=False)
        except FieldError:
            return self.model.MixinQuerySet(self.model)

class BaseMixin(models.Model):
    admin = models.Manager()
    objects = MixinManager()

    class MixinQuerySet(QuerySet):

        def globals(self):
            try:
                return self.filter(is_global=True)
            except FieldError:
                return self.all()

    class Meta:
        abstract = True

class DeleteMixin(BaseMixin):
    deleted = models.BooleanField(default=False)

    class Meta:
        abstract = True

    def delete(self):
        self.deleted = True
        self.save()

class GlobalMixin(BaseMixin):
    is_global = models.BooleanField(default=True)

    class Meta:
        abstract = True
Run Code Online (Sandbox Code Playgroud)

将来想要为查询集添加额外功能的任何mixin只需要扩展BaseMixin(或者在其heirarchy中的某个地方).每当我尝试过滤查询设置时,我将其包装在try-catch中,以防该字段实际上不存在(即,它不会扩展该mixin).使用globals()调用全局过滤器,同时自动调用删除过滤器(如果删除某些内容,我绝不希望它显示).使用此系统允许以下类型的命令:

TemporaryModel.objects.all() # If extending DeleteMixin, no deleted instances are returned
TemporaryModel.objects.all().globals() # Filter out the private instances (non-global)
TemporaryModel.objects.filter(...) # Ditto about excluding deleteds
Run Code Online (Sandbox Code Playgroud)

需要注意的一点是,删除过滤器不会影响管理界面,因为首先声明默认管理器(使其成为默认值).我不记得他们何时将管理员更改为使用Model._default_manager而不是Model.objects,但任何已删除的实例仍将显示在管理员中(如果您需要取消删除它们).