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

egdjgwm8  于 2022-11-18  发布在  Go
关注(0)|答案(3)|浏览(115)

我想知道哪些模型是一个模型的子模型,以检索它们的on_delete属性。如我所知,如下所示,如果我们有ownerModel,它是childModel1check1Model的父模型:

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)

我的意思是通过检查field键是否在ownerModel._meta.get_fields()项中的__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键是在__dict__.keys()项中的ownerModel._meta.get_fields()项中“则得到子项信息。
但问题是,在某些情况下,我无法从父模型中获得子模型的信息。
1.这让我想知道这2个条件是否足以发现哪些模特是模特的孩子?
1.是否有其他类似的方法来获取哪些模型是模型的子模型?
顺便说一下,我也想拥有on_delete,拥有on_delete是我使用_meta.get_fields()而不是_meta.fields的唯一原因,因为_meta.fields不提供on_delete属性。
这是my code,如果你想看一下的话。注意,在完整的答案中,我还想知道是什么造成了问题。所以在这个例子中,__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()提供这些关系信息而其他时候不提供的原因是什么。

2izufjch

2izufjch1#

可以使用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

wgeznvg7

wgeznvg72#

更新

在进一步调查评论后,发现问题是模型没有在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__并不适合这种用法。例如,这是not reliable,没有理由在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关系。

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()
gmol1639

gmol16393#

如果你想检索这个模型的反向关系,你可以使用ownerModel._meta.related_objects。但是这是Django私有API的一部分,没有正式的文档/支持。你可以在源代码中找到文档。
返回指向当前模型的所有相关对象。相关对象可以来自一对一、一对多或多对多字段关系类型。
仅由Django自己使用的私有API; get_fields()与字段属性的过滤相结合是用于获取此字段列表的公共API。
您也可以通过使用公共API过滤get_fields()来获得类似的结果:

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)

相关问题