更改数据的Django自定义管理器方法与其他管理器的方法(过滤器,获取等)不正确地工作

时间:2017-12-21 13:12:17

标签: python django django-models django-managers

我创建了一个自定义Manager,但在我的情况下,它的方法在模型中的特定字段末尾添加了一些字符串,而不是像通常情况那样只过滤查询集。

我的目标是在调用SomeModel.objects时返回已更改的对象。 Django的文档说:

  

您可以通过覆盖Manager.get_queryset()方法来覆盖Manager的基本QuerySet。 get_queryset()应该返回一个QuerySet,其中包含您需要的属性。

当我致电SomeModel.objects.all()时,我的方法有效,但如果我为objects.all()之后应用了一些过滤器,我可以看到数据变得正常。

models.py:

class BaseModelQuerySet(models.QuerySet):
    def edit_desc(self, string):
        if self.exists():
            for obj in self:
                if 'description' in obj.__dict__:
                    obj.__dict__['description'] += string
        return self

class BaseModelManager(models.Manager):
    def get_queryset(self):
        return BaseModelQuerySet(self.model, using=self._db).edit_desc('...extra text')


class BaseModel(models.Model):
   objects = BaseModelManager()

    class Meta:
        abstract = True

Shell输出:

>>> Macro.objects.all()[0].description
'Test text...extra text'
>>> Macro.objects.all().filter(id=1)[0].description
'Test text'

这让我感到困惑。这种印象是其他方法调用常规查询集而不是使用自定义objects返回的方法。

2 个答案:

答案 0 :(得分:3)

返回查询集的Queryset方法实际上返回 new 查询集(它们不会更改当前的查询集)。并不是说他们正在回归"常规" querysets,它们是自定义查询集子类的实例(您可以通过检查查询集类型自行检查),但尚未调用edit_desc()方法。

从技术上讲,你可以解决&#34;这可以通过覆盖所有exclude / filter / ect方法来重新应用你的edit_desc()方法,但它会非常低效(甚至比它实际上更多),所以你可能想重新考虑你的 real < / em>问题是如何以更有效和更少侵入性的方式解决它。或许解释一下您的具体用例可以为如何解决它提供更好的答案 ?

编辑:鉴于您的评论,一个可能(并且更有效)的解决方案是覆盖产生或返回模型实例的QuerySet部分(或仅仅是原始值),以便您可以处理模型实例/此时的数据。您可能需要查看django / db / query.py,特别是ModelIterableValueIterable类。这比你目前的解决方案要多得多,但是很好......

如果您只关心模型实例(而不是原始数据),另一个可能更简单的解决方案可能是使用ModelProxy,覆盖它的__init__方法来添加您的处理。 ..

答案 1 :(得分:0)

最后我意识到, queryset 方法实际上并不适用于 queryset 对象。相反,他们会添加一些额外的 SQL 语法, 再次向数据库执行查询

因此,更改对象的数据而不保存它,然后尝试执行某些过滤或等 将导致再次查询数据库并检索初始数据

如果有人在我的问题中有类似的用例,那么不幸的是另一种方法需要。可能的是:

  • 重新定义查询集方法,使它们在查询集localy上工作,就像列表一样(不是preffered,可能导致默认django行为中的各种问题)
  • 创建额外的模型,与主模型逻辑相关(可能很好)

无论如何,非常感谢 @bruno desthuilliers 以及他的建议,如果有人有更好的想法,我们很乐意在评论中看到它们。