带有on_delete属性的Django模型关系信息



我想知道哪些模型是模型的子模型,以便检索它们的on_delete属性。如我所知,如下图所示,如果ownerModelchildModel1check1Model的母体:

import uuid
from django.db import models
class ownerModel(models.Model):
    ownerId = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False, blank=True)
class check1Model(models.Model):
    checkId = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False, blank=True)
    owner=models.ForeignKey(ownerModel,on_delete=models.CASCADE)
class childModel1(models.Model):
    childId = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False, blank=True)
    check2=models.ForeignKey(ownerModel,on_delete=models.CASCADE)

然后我们可以得到ownerModel的子模型,代码如下:

class myView(views.APIView):
    def get(self, request, format=None):
        for f in ownerModel._meta.get_fields():
            if 'field' in f.__dict__.keys():
                print('***childModels***')
                print(f.__dict__)
                print()
        return Response({'0000'}, status=status.HTTP_200_OK)

我的意思是在ownerModel._meta.get_fields()的项中检查field键是否在__dict__.keys()中。

当然,这里我们得到了关于子模型的扩展信息:

***childModels***
{'field': <django.db.models.fields.related.ForeignKey: owner>, 'model': <class 'Users.models.ownerModel'>, 'related_name': None, 'related_query_name': None, 'limit_choices_to': {}, 'parent_link': False, 'on_delete': <function 
CASCADE at 0x00000286550848B0>, 'symmetrical': False, 'multiple': True, 'field_name': 'ownerId', 'related_model': <class 'Users.models.check1Model'>, 'hidden': False}
***childModels***
{'field': <django.db.models.fields.related.ForeignKey: check2>, 'model': <class 'Users.models.ownerModel'>, 'related_name': None, 'related_query_name': None, 'limit_choices_to': {}, 'parent_link': False, 'on_delete': <function CASCADE at 0x00000286550848B0>, 'symmetrical': False, 'multiple': True, 'field_name': 'ownerId', 'related_model': <class 'Users.models.childModel1'>, 'hidden': False}

所以我发现这两个条件是获得子模型信息所必需的:

  1. 在子模型中,确保使用如下行设置子关系:
models.ForeignKey(ownerModel,on_delete=models.CASCADE)
  1. 如前所述,如果field键在ownerModel._meta.get_fields()项的__dict__.keys()中;获取儿童信息。

但问题是,在某些情况下,我不能从父模型获得子信息。所以:

  1. 这让我想知道这两个条件是否足以找出哪些模型是模型的孩子?
  2. 是否有其他类似的方法来获得哪些模型是模型的孩子?

顺便说一下,我也想有on_delete,并且有on_delete是我使用_meta.get_fields()超过_meta.fields的唯一原因,因为_meta.fields不提供on_delete属性。

这是我的代码,如果你想看一下。注意,在完整的回答中,我也想知道是什么造成了问题。因此,在这种情况下,__dict__.keys()不提供键中没有field的项(不提供子模型细节)。因为通常这两个条件提供了子模型的细节。所以以后我可以在所有代码中获得子模型的详细信息。

问题是,即使有for f in ownerModel._meta.get_fields(include_hidden=True),没有任何进一步的if s不检索行包括on_delete属性在这个项目。但在其他项目中,ownerModel._meta.get_fields()提供了它们。我不知道ownerModel._meta.get_fields()有时提供这些关系信息,有时不提供的原因。

您可以使用Model._meta.related_objects查找模型的子节点

# Example Code
from django.utils.functional import classproperty
from django.db import models
class SomeModel(models.Model):
    class Meta:
        abstract = True
    
    @classproperty
    def related_objects(cls):
        """
        Return list of related models within the same module
        ManyToManyRel not supported
        """
        related_objects = [
            rel
            for rel in cls._meta.related_objects
            if isinstance(rel, (models.ManyToOneRel, models.OneToOneRel))
        ]
        return [
            (rel.related_model, rel.on_delete)
            for rel in related_objects
            if rel.related_model._meta.app_label == cls._meta.app_label
        ]

你可以使用这个类作为你的模型的mro

更新

在评论中进一步调查后,发现问题是模型没有在models子模块中定义。如Django文档中所述:

一旦你定义了你的模型,你需要告诉Django你将要使用这些模型。为此,编辑您的设置文件并更改INSTALLED_APPS设置,以添加包含models.py的模块名称。.

这个要求也可以在Django处理模型检测的源代码中得到确认:

MODELS_MODULE_NAME = "models"

class AppConfig:
    ...
    def import_models(self):
        # Dictionary of models for this app, primarily maintained in the
        # 'all_models' attribute of the Apps this AppConfig is attached to.
        self.models = self.apps.all_models[self.label]
        if module_has_submodule(self.module, MODELS_MODULE_NAME):
            models_module_name = "%s.%s" % (self.name, MODELS_MODULE_NAME)
            self.models_module = import_module(models_module_name)

原始回答

没有明显的理由为什么使用__dict__不能工作。然而,__dict__并不是真正用于这种用法的。这是不可靠的,没有理由在hasattr()上使用它。

以下是__dict__可能出错的几个例子:

In [1]: class A:
   ...:     def __init__(self):
   ...:         self.foo = 'foobaz'
   ...:         self.bar = 'barbaz'
   ...: 
In [2]: a = A()
In [3]: a.__dict__
Out[3]: {'foo': 'foobaz', 'bar': 'barbaz'}
In [4]: class B(A):
   ...:     @property
   ...:     def bar(self):
   ...:         return 'barfoo'
   ...:     @bar.setter
   ...:     def bar(self, value):
   ...:         pass
   ...: 
In [5]: b = B()
In [6]: b.__dict__
Out[6]: {'foo': 'foobaz'}
# B redefined 'bar' as a property, which removed it from __dict__
In [7]: 'bar' in b.__dict__
Out[7]: False
In [8]: hasattr(b, 'bar')
Out[8]: True
In [9]: class C(A):
   ...:     __slots__ = ('foo', 'bar')
   ...: 
In [10]: c = C()
In [11]: c.foo
Out[11]: 'foobaz'
In [12]: c.bar
Out[12]: 'barbaz'
In [13]: c.__dict__
Out[13]: {}
# C defined 'foo' and 'bar' as slots, which removed them from __dict__
In [14]: hasattr(c, 'foo')
Out[14]: True

你可以使用hasattr():

for f in ownerModel._meta.get_fields():
    if hasattr(f, 'field'):
        print(f, f.on_delete)
        print()

然而,检查field属性的存在似乎并不是确定字段是否为向后关系的可靠解决方案。我宁愿推荐依赖one_to_oneone_to_many属性,它们是非关系字段的None,关系字段的True/False:

for f in ownerModel._meta.get_fields():
    if (f.one_to_one or f.one_to_many) and hasattr(f, 'on_delete'):
        print(f, f.on_delete)
        print()

我还在条件中包含了hasattr(f, 'on_delete'),以排除ownerModel中定义的OneToOneField关系。不过,测试isinstance(f, ForeignObjectRel)可能更简洁:

from django.db.models import ForeignObjectRel
for f in ownerModel._meta.get_fields():
    if isinstance(f, ForeignObjectRel) and (f.one_to_one or f.one_to_many):
        print(f, f.on_delete)
        print()

如果您想检索到这个模型的反向关系,您可以使用ownerModel._meta.related_objects。然而,这是Django私有API的一部分,没有官方文档/支持。您还可以在源代码中找到文档。

返回所有指向当前模型的相关对象。相关对象可以来自一对一、一对多或多对多的字段关系类型。

私有API,仅供Django自己使用;get_fields()结合字段属性过滤是获取该字段列表的公共API。

您也可以通过过滤get_fields():

使用公共API获得类似的结果
related_objects = [
    f for f in ownerModel._meta.get_fields(include_hidden=True)
    if (not f.hidden and (f.one_to_one or f.one_to_many)) or f.many_to_many
]

此解决方案还为您提供了在ownerModel中定义的多对多关系,如果您想要多对多,这可能是预期的。要排除所有的多对多关系,只需从条件中删除它们:

related_objects = [
    f for f in ownerModel._meta.get_fields(include_hidden=True)
    if not f.hidden and (f.one_to_one or f.one_to_many)
]

一种严格地只得到反向关系的方法(即。删除ownerModel中定义的多对多和一对一关系),可能是为了测试ForeignObjectRel在字段层次结构中的存在。

from django.db.models import ForeignObjectRel
related_objects = [
    f for f in ownerModel._meta.get_fields(include_hidden=True)
    if isinstance(f, ForeignObjectRel) and (not f.hidden or f.many_to_many)
]

如果您想要显示on_delete属性的值,您可以将结果限制为一对一和一对多字段。

from django.db.models import ForeignObjectRel
related_objects = [
    f for f in ownerModel._meta.get_fields()
    if isinstance(f, ForeignObjectRel)
    and (f.one_to_one or f.one_to_many)
]
for f in related_objects:
    print(f, f.on_delete)

最新更新