为什么以及为什么我不能在Django上覆盖相关的管理器方法?

时间:2018-08-24 22:22:03

标签: python django orm entitymanager

我有这个经理:

class ConfigValueManager(models.Manager):

    def get(self, key):
        config_value = self.filter(key=key).first()
        if config_value:
            type_caster = locate(config_value.type)
            return type_caster(config_value.value)
        return config_value

    def set(self, key, value):
        self.filter(key=key).update(value=value)

    def set2(self, key, value):
        qs = self.filter(key=key)
        if qs:
            qs.update(value=value, type=type(value).__name__, company=self.instance)
        else:
            self.create(key=key, value=value, type=type(value).__name__, company=self.instance)

问题是我无法覆盖set。即使我已经在孩子上创建了set,该方法仍然来自父级。有趣的是getset2很好。即使是add,在我的示例中也不能被覆盖。

我的问题是如何覆盖set以及为什么会发生这种情况?

2 个答案:

答案 0 :(得分:0)

models.py

from django.db import models
from django.db.models.query import QuerySet


class PersonQuerySet(QuerySet):
    def set(self, slug, **kwargs):
        return self.filter(slug=slug).update(**kwargs)


class Person(models.Model):
    name = models.CharField(max_length=100, null=True)
    slug = models.CharField(max_length=10, null=True)

    objects = PersonQuerySet.as_manager()

tests.py

from django.test import TestCase
from core.models import Person

class TestSet(TestCase):
    def test_just_update_records_with_the_same_slug(self):
        Person.objects.create(slug='batman', name='John')
        Person.objects.create(slug='batman', name='Connor')
        Person.objects.create(slug='bruce', name='Ill be back')

        Person.objects.set('batman', name='###')

        expected_value = 2
        result = Person.objects.filter(name='###').count()

        self.assertEqual(result, expected_value)

github示例

https://github.com/luivilella/django-test-manager

答案 1 :(得分:0)

我添加了一些为什么不容易实现的细节,因为我在同一问题上苦苦挣扎。 正如我们在django source code中看到的那样,set(如addcreate)在动态创建的RelatedManager中被覆盖。该RelatedManager实际上将我们的经理用作super class,这就是可以使用您的getset2方法的原因,但对于覆盖的方法却无济于事。

此管理器是在ReverseManyToOneDescriptor.related_manager_cls缓存的属性中创建的。在您的github片段上的示例中,Company.config_values是此ReverseManyToOneDescriptor的实例。

我将通过对您的代码进行一些假设来展示一个有关如何覆盖set方法的示例,因为它缺少某些定义(例如Company模型,ForeignKey FooConfigValue内的字段。)

我不建议使用它,因为它对django更改绝对不可靠,并且我也没有做任何测试,它只是证明如何创建RelatedManager实例 在示例代码的末尾添加它,它应该可以工作

def modify_related_manager_set(model_cls):
    # model_cls = Company here, and config_values is the related field name
    reverse_descriptor = model_cls.config_values
    base_set = reverse_descriptor.related_manager_cls.set

    def custom_set(*args, **kwargs):
        print("in my custom set")
        return base_set(*args, **kwargs)

    reverse_descriptor.related_manager_cls.set = custom_set
# do this call after all the models have been created
# e.g. after defining FooConfigValue
 modify_related_manager_set(Company) 

现在您应该看到in my custom set正在打印。

我知道这没有多大帮助,但至少可以帮助您了解相关字段的工作方式