Jas*_*enX 7 django django-models
使用Django 3.2-- 我将尽可能简化问题。
我有三个模型类:
# abstract base class
MyAbstractModel(models.Model)
# derived model classes
Person(MyAbstractModel)
LogoImage(MyAbstractModel)
Run Code Online (Sandbox Code Playgroud)
每个Person都有:
image = ForeignKey(LogoImage, db_index=True, related_name="person", null=True,
on_delete=models.PROTECT)
Run Code Online (Sandbox Code Playgroud)
定义MyAbstractModel了一些模型管理器:
objects = CustomModelManager()
objects_all_states = models.Manager()
Run Code Online (Sandbox Code Playgroud)
以及一个state字段,可以是active或者inactive
CustomModelManager 被定义为仅带来状态 == 'active' 的记录:
class CustomModelManager(models.Manager):
def get_queryset(self):
return super().get_query().filter(self.model, using=self._db).filter(state='active')
Run Code Online (Sandbox Code Playgroud)
在我的数据库中,两个表中有两个对象:
Person ID 1 state = 'active'
Image ID 1 state = 'inactive'
Run Code Online (Sandbox Code Playgroud)
Person ID 1Image ID 1通过字段有外键连接Person.image。
------ 现在讨论这个问题 ----------------
# CORRECT: gives me the person object
person = Person.objects.get(id=1)
# INCORRECT: I get the image, but it should not work...
image = person.image
Run Code Online (Sandbox Code Playgroud)
为什么这是不正确的?因为我使用模型管理器查询人员对象,objects该管理器应该只带来那些具有active状态的项目。它带来了 ,这Person很好,因为Person (ID=1)是state==active- 但下面的对象person.image是state==inactive。为什么我会得到它?
解决方法尝试:
添加base_manager_name = "objects"到该MyAbstractModel class Meta:部分
再次尝试:
# CORRECT: gives me the person object
person = Person.objects.get(id=1)
# CORRECT: gives me a "Does not Exist" exception.
image = person.image
Run Code Online (Sandbox Code Playgroud)
然而......现在我尝试这个:
# CORRECT: getting the person
person.objects_all_states.get(id=1)
# INCORRECT: throws a DoesNotExist, as it's trying to use the `objects` model manager I hard coded in the `MyAbstractModel` class meta.
image = person.image
Run Code Online (Sandbox Code Playgroud)
objects_all_states因为我得到了不关心的人——我希望我也会以类似的方式state==active得到。person.image但这并没有按预期工作。
根本问题
如何强制使用相同的模型管理器来获取父对象(Person)——在获取 a 拥有的每个ForeignKey对象时Person?我找不到答案。我这几天一直在兜圈子。任何地方都没有明确的答案。要么我错过了一些非常基本的东西,要么 Django 有一个设计缺陷(当然我并不真正相信)——那么,我在这里错过了什么?
_base_manager:\nreturn self.field.remote_field.model._base_manager.db_manager(hints=hints).all()\nRun Code Online (Sandbox Code Playgroud)\n...哪里hints会是{\'instance\': <Person: Person object (1)>}。由于我们有对父级的引用,因此在某些情况下,我们可以支持这个推论。
\nDjango 特别提到不要这样做。
\n来自django.db.models.Model._base_manager:
\n\n\nDon\xe2\x80\x99t 过滤掉此类管理器子类中的任何结果
\n该管理器用于访问与其他模型相关的对象。在这些情况下,Django 必须能够看到它正在获取的模型的所有对象,以便可以检索引用的任何内容。
\n因此,您不应覆盖
\nget_queryset()以过滤掉任何行。如果这样做,Django 将返回不完整的结果。
你可以:
\nget()以主动存储有关实例的一些信息(将作为提示传递),了解是否CustomModelManager使用 的实例来获取它,然后get_queryset,检查并尝试回退到objects_all_states.class CustomModelManager(models.Manager):\n\n def get(self, *args, **kwargs):\n instance = super().get(*args, **kwargs)\n instance.hint_manager = self\n return instance\n\n def get_queryset(self):\n hint = self._hints.get(\'instance\')\n if hint and isinstance(hint.__class__.objects, self.__class__):\n hint_manager = getattr(hint, \'hint_manager\', None)\n if not hint_manager or not isinstance(hint_manager, self.__class__):\n manager = getattr(self.model, \'objects_all_states\', None)\n if manager:\n return manager.db_manager(hints=self._hints).get_queryset()\n return super().get_queryset().filter(state=\'active\')\nRun Code Online (Sandbox Code Playgroud)\n这可能不起作用的许多边缘情况之一是如果您person通过Person.objects.filter(id=1).first().
用法:
\nperson = Person.objects_all_states.get(id=1)\n# image = person.image\nwith CustomModelManager.disable_for_instance(person):\n image = person.image\nRun Code Online (Sandbox Code Playgroud)\n执行:
\nclass CustomModelManager(models.Manager):\n _disabled_for_instances = set()\n\n @classmethod\n @contextmanager\n def disable_for_instance(cls, instance):\n is_already_in = instance in cls._disabled_for_instances\n if not is_already_in:\n cls._disabled_for_instances.add(instance)\n yield\n if not is_already_in:\n cls._disabled_for_instances.remove(instance)\n\n def get_queryset(self):\n if self._hints.get(\'instance\') in self._disabled_for_instances:\n return super().get_queryset()\n return super().get_queryset().filter(state=\'active\')\nRun Code Online (Sandbox Code Playgroud)\n用法:
\n# person = Person.objects_all_states.get(id=1)\n# image = person.image\nwith CustomModelManager.disable():\n person = Person.objects.get(id=1)\n image = person.image\nRun Code Online (Sandbox Code Playgroud)\n执行:
\nimport threading\nfrom contextlib import contextmanager\n\nfrom django.db import models\nfrom django.utils.functional import classproperty\n\n\nclass CustomModelManager(models.Manager):\n _data = threading.local()\n\n @classmethod\n @contextmanager\n def disable(cls):\n is_disabled = cls._is_disabled\n cls._data.is_disabled = True\n yield\n cls._data.is_disabled = is_disabled\n\n @classproperty\n def _is_disabled(cls):\n return getattr(cls._data, \'is_disabled\', None)\n\n def get_queryset(self):\n if self._is_disabled:\n return super().get_queryset()\n return super().get_queryset().filter(state=\'active\')\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
742 次 |
| 最近记录: |