Django重写多对多字段模型管理器

时间:2022-10-04 13:51:43

How can i override the model manager of a many-to-many field that i have considering the following:

我如何重写一个多对多字段的模型管理器,我将考虑以下内容:

class TermsManager(models.Manager):
    def all(self):
        return super(TermsManager, self).all().filter(condition_here)


class Term(models.Model):
    objects = TermsManager()

    name = models.CharField(max_length=255)

class Object(models.Model):        
    title = models.CharField(max_length=255)
    terms = models.ManyToManyField(Term, blank=True)

class Channel(Object):
    class Meta:
        proxy = True

I also have a class which inherits from TermManager called ChannelTermManager. How can i override the "terms" field of the Channel model so that mychannel.terms calls the ChannelTermManager instead of TermManager?

我还有一个继承自TermManager的类,叫做ChannelTermManager。如何覆盖通道模型的“terms”字段,使之成为mychannel。术语调用ChannelTermManager而不是TermManager?

1 个解决方案

#1


2  

First of all, you shouldn't be overriding all(). If you want to change the default queryset, override get_query_set like so:

首先,您不应该重写all()。如果要更改默认的queryset,请按以下方式覆盖get_query_set:

class TermsManager(models.Manager):
    def get_query_set(self):
        return super(TermsManager, self).get_query_set().filter(condition_here)

This is because all() is often omitted when other queryset functions are chained on, and you want your queryset to behave the same whether all() is explicitly called or not.

这是因为在连接其他queryset函数时,常常忽略all(),并且无论是否显式调用all(),都希望您的queryset的行为相同。

But even so, what you're doing is still problematic. As explained in the documentation for managers, filtering the default related queryset will affect all sorts of automatic things behind the scenes (such as when dumping data to create backups/fixtures, etc.). You almost definitely do not want this. And you really don't want your related object managers doing this either (by setting use_for_related_fields = True), because you'll be masking what's actually stored in the database, rather than simply detecting out of date data and creating alerts or whatever to clean it up. use_for_related_fields is intended for creating managers that augment the normal capabilities of the vanilla manager, not to filter.

但即便如此,你所做的仍然是有问题的。正如在管理人员的文档中所解释的,过滤默认相关的queryset将会影响到幕后的各种自动操作(例如当转储数据以创建备份/fixture等)。你肯定不想要这个。而且您也不希望相关的对象管理器这样做(通过设置use_for_related_fields = True),因为您将屏蔽数据库中实际存储的内容,而不是简单地检测过时的数据并创建警报或其他清理它的方法。use_for_related_fields是用于创建增强普通管理器的正常功能的管理器,而不是用于过滤。

I had a similar situation to yours however, and I handled it like so:

我和你的情况差不多,我是这样处理的:

class FilteredTermsManager(models.Manager):
    def get_query_set(self):
        return super(TermsManager, self).get_query_set().filter(condition_here)

class Term(models.Model):
    allTerms = models.Manger() # Establish this as the default/automatic manager
    objects = FilteredTermsManager()

    name = models.CharField(max_length=255)

This way, I could do all my initial querying on the model through my filtered queryset and it looks like "regular Django", but all relational and behind the scenes queries would work on the unfiltered database. And I could always access the true full set of objects by manually doing Term.allTerms.all().

通过这种方式,我可以通过过滤的queryset对模型进行所有初始查询,它看起来像“常规的Django”,但是所有的关系查询和后台查询都可以在未过滤的数据库上工作。而且我可以通过手工做Term.allTerms.all()来访问真正的完整的对象集。

As for using different managers for different related objects, there's nothing you can really do there. But why not just add Channel specific objects to your custom manager, and simply not call them from methods that operate on get Term querysets from Object?

对于为不同的相关对象使用不同的管理器,您实际上没有什么可以做的。但是为什么不直接将通道特定对象添加到自定义管理器中,而不直接从操作get Term querysets的方法中调用它们呢?

#1


2  

First of all, you shouldn't be overriding all(). If you want to change the default queryset, override get_query_set like so:

首先,您不应该重写all()。如果要更改默认的queryset,请按以下方式覆盖get_query_set:

class TermsManager(models.Manager):
    def get_query_set(self):
        return super(TermsManager, self).get_query_set().filter(condition_here)

This is because all() is often omitted when other queryset functions are chained on, and you want your queryset to behave the same whether all() is explicitly called or not.

这是因为在连接其他queryset函数时,常常忽略all(),并且无论是否显式调用all(),都希望您的queryset的行为相同。

But even so, what you're doing is still problematic. As explained in the documentation for managers, filtering the default related queryset will affect all sorts of automatic things behind the scenes (such as when dumping data to create backups/fixtures, etc.). You almost definitely do not want this. And you really don't want your related object managers doing this either (by setting use_for_related_fields = True), because you'll be masking what's actually stored in the database, rather than simply detecting out of date data and creating alerts or whatever to clean it up. use_for_related_fields is intended for creating managers that augment the normal capabilities of the vanilla manager, not to filter.

但即便如此,你所做的仍然是有问题的。正如在管理人员的文档中所解释的,过滤默认相关的queryset将会影响到幕后的各种自动操作(例如当转储数据以创建备份/fixture等)。你肯定不想要这个。而且您也不希望相关的对象管理器这样做(通过设置use_for_related_fields = True),因为您将屏蔽数据库中实际存储的内容,而不是简单地检测过时的数据并创建警报或其他清理它的方法。use_for_related_fields是用于创建增强普通管理器的正常功能的管理器,而不是用于过滤。

I had a similar situation to yours however, and I handled it like so:

我和你的情况差不多,我是这样处理的:

class FilteredTermsManager(models.Manager):
    def get_query_set(self):
        return super(TermsManager, self).get_query_set().filter(condition_here)

class Term(models.Model):
    allTerms = models.Manger() # Establish this as the default/automatic manager
    objects = FilteredTermsManager()

    name = models.CharField(max_length=255)

This way, I could do all my initial querying on the model through my filtered queryset and it looks like "regular Django", but all relational and behind the scenes queries would work on the unfiltered database. And I could always access the true full set of objects by manually doing Term.allTerms.all().

通过这种方式,我可以通过过滤的queryset对模型进行所有初始查询,它看起来像“常规的Django”,但是所有的关系查询和后台查询都可以在未过滤的数据库上工作。而且我可以通过手工做Term.allTerms.all()来访问真正的完整的对象集。

As for using different managers for different related objects, there's nothing you can really do there. But why not just add Channel specific objects to your custom manager, and simply not call them from methods that operate on get Term querysets from Object?

对于为不同的相关对象使用不同的管理器,您实际上没有什么可以做的。但是为什么不直接将通道特定对象添加到自定义管理器中,而不直接从操作get Term querysets的方法中调用它们呢?