Django经理链接

2024-10-17 06:20:48 发布

您现在位置:Python中文网/ 问答频道 /正文

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

我有多个抽象模型类,用来为其他模型提供小的、特定的功能。其中两个模型是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()

基本上,它提供了一个伪删除(deleted标志),而不是实际删除对象。

GlobalMixin的定义如下:

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

    objects = GlobalManager()

    class Meta:
        abstract = True

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

它们都有自己的管理器,影响返回的queryset。我的DeleteManager将queryset过滤为只返回deleted标志设置为False的结果,而GlobalManager将queryset过滤为只返回标记为全局的结果。以下是这两种情况的声明:

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)

期望的功能是让一个模型扩展这两个抽象模型,并允许只返回未删除和全局的结果。我在一个有4个实例的模型上运行了一个测试用例:一个是全局和非删除的,一个是全局和删除的,一个是非全局和非删除的,还有一个是非全局和删除的。如果我试图得到结果集:SOMMODEL。对象。(),我得到实例1和3(两个未删除的-太好了!)如果我尝试SomeModel.objects.globals(),就会得到一个DeleteManager没有globals的错误(这是假设我的模型声明是这样的:SomeModel(DeleteMixin,GlobalMixin)。如果我颠倒顺序,我不会得到错误,但它不会过滤掉删除的错误)。如果我将GlobalMixin更改为将GlobalManager附加到globals而不是对象(因此新命令将是SomeModel.globals.globals()),我将获得实例1和2(两个globals),而我的预期结果将是仅获取实例1(全局的、未删除的)。

我不确定是否有人遇到过类似的情况,并取得了结果。无论是让它在我当前的思维中工作,还是提供我所追求的功能的重新工作,都将非常感谢。我知道这篇文章有点冗长。如果需要更多的解释,我很乐意提供。

编辑:

我已经在下面发布了我对这个特定问题的最终解决方案。它基于到Simon的自定义QuerySetManager的链接。


Tags: 对象实例模型selftrue管理器models全局
3条回答

在Djangosnippets上看到这个片段:http://djangosnippets.org/snippets/734/

不要将自定义方法放在管理器中,而是对queryset本身进行子类化。它非常简单,工作得很好。我遇到的唯一问题是模型继承,您总是必须在模型子类中定义管理器(子类中的“objects=queryset manager()”),即使它们将继承queryset。使用QuerySetManager后,这将更有意义。

下面是使用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

将来任何想向查询集添加额外功能的mixin都只需要扩展BaseMixin(或者将其放在其继承结构中的某个位置)。每当我试图过滤设置的查询时,我都会将它包装在一个try catch中,以防该字段实际上不存在(即,它不扩展该mixin)。全局过滤器是使用globals()调用的,而delete过滤器是自动调用的(如果删除了某些内容,我永远不希望它显示出来)。使用此系统可以执行以下类型的命令:

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

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

我花了一段时间想办法建一个好工厂,但我遇到了很多问题。

我能给你的建议是把你的遗产连锁起来。它不是很通用的,所以我不确定它有多有用,但你要做的就是:

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

    objects = GlobalManager()

    class Meta:
        abstract = True

class GlobalManager(DeleteManager):
    def globals(self):
        return self.get_query_set().filter(is_global=1)

如果你想要更通用的东西,我能想到的最好方法是定义一个基MixinManager来重新定义get_query_set()(我假设你只想这样做一次;否则事情会变得非常复杂),然后通过Mixin传递一个要添加的字段列表

它看起来像这样(根本没有经过测试):

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

    class Meta:
        abstract = True

def create_mixin(base_mixin, **kwargs):
    class wrapper(base_mixin):
        class Meta:
            abstract = True
    for k in kwargs.keys():
        setattr(wrapper, k, kwargs[k])
    return wrapper

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

def create_manager(base_manager, **kwargs):
    class wrapper(base_manager):
        pass
    for k in kwargs.keys():
        setattr(wrapper, k, kwargs[k])
    return wrapper

好吧,这很难看,但你觉得怎么样?从本质上说,这是相同的解决方案,但更具活力,也更干燥,尽管阅读起来更复杂。

首先,动态创建管理器:

def globals(inst):
    return inst.get_query_set().filter(is_global=1)

GlobalDeleteManager = create_manager(DeleteManager, globals=globals)

这将创建一个新的管理器,它是DeleteManager的子类,并且有一个名为globals的方法。

接下来,创建mixin模型:

GlobalDeleteMixin = create_mixin(DeleteMixin,
                                 is_global=models.BooleanField(default=False),
                                 objects = GlobalDeleteManager())

就像我说的,很难看。但这意味着你不必重新定义globals()。如果您希望不同类型的管理器具有globals(),只需使用不同的基再次调用create_manager。你可以添加任意多的新方法。同样对于管理器,您只需不断添加新函数,这些函数将返回不同的queryset。

那么,这真的实用吗?也许不是。这个答案更多的是(ab)使用Python的灵活性。我没有尝试过使用它,尽管我确实使用了一些动态扩展类的基本原理,以使访问更容易。

如果有什么不清楚的地方请告诉我,我会更新答案。

相关问题 更多 >