简体   繁体   English

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

[英]How and why I can't override related manager method on django?

I have this manager:我有这个经理:

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)

the problem is that I can't overwrite set .问题是我无法覆盖set The method is still coming from the parent, even though I've created set on the child.该方法仍然来自父级,即使我已经在子级上创建了set Funny thing is that get and set2 are fine.有趣的是getset2问题。 Even add which isn't in my example can't be overridden.即使add不在我的示例中也不能被覆盖。

My question is how can I overwrite set and why this happens?我的问题是如何覆盖set以及为什么会发生这种情况?

I add some details on why it's not easily possible, because I struggled on the same issue.我添加了一些细节,说明为什么这不容易实现,因为我在同一个问题上苦苦挣扎。 set , like add or create , are overridden in the dynamically created RelatedManager , as we can see in the django source code . setaddcreate一样,在动态创建的RelatedManager中被覆盖,正如我们在django 源代码中看到的那样。 This RelatedManager actually uses our manager as a super class that's why your get and set2 methods can used, but it does not help for overridden methods.这个RelatedManager实际上将我们的管理器用作超类,这就是可以使用getset2方法的原因,但它对重写方法没有帮助。

This manager is created in the ReverseManyToOneDescriptor.related_manager_cls cached property.该管理器在ReverseManyToOneDescriptor.related_manager_cls缓存属性中创建。 In the example on your github snippet, Company.config_values is an instance of this ReverseManyToOneDescriptor .在您的 github 片段示例中, Company.config_values是此ReverseManyToOneDescriptor的一个实例。

I'll show an example on how to override the set method, by making some assumptions on your code, because it misses some definitions (like the Company model, the ForeignKey field inside FooConfigValue .)我将通过对您的代码做出一些假设来展示如何覆盖set方法的示例,因为它遗漏了一些定义(例如Company模型, FooConfigValue中的ForeignKey字段。)

I don't advise to use it, as it's absolutely not robust against django changes, and I didn't do any test, it just serves as a proof on how RelatedManager instances are created Add this at the end of the example code and it should work我不建议使用它,因为它对 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) 

And you should now see the in my custom set being printed.你现在应该看到in my custom set正在打印。

I know this doesn't help much, but at least it helped understand how related fields work我知道这没什么帮助,但至少有助于理解相关领域的工作原理

models.py模型.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测试.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 example github例子

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM