Django получает, какие модели являются дочерними для данной модели
Я хочу знать, какие модели являются дочерними для той или иной модели. Как я знаю, если у нас есть ownerModel
, которая является родителем childModel1
и check1Model
:
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
в __dict__.keys()
в элементах ownerModel._meta.get_fields()
ofc так, как здесь мы получаем расширенную информацию о детских моделях:
***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}
поэтому я нахожу эти 2 условия необходимыми для получения информации о детских моделях:
- в дочерних моделях убедитесь, что дочерние отношения установлены с помощью строки, как показано ниже:
models.ForeignKey(ownerModel,on_delete=models.CASCADE)
- как сказано "если ключ
field
находится в__dict__.keys()
в элементахownerModel._meta.get_fields()
" для получения информации о детях.
но проблема в том, что в некоторых случаях я не могу получить информацию о детях из родительской модели. так:
мне интересно, достаточно ли этих двух условий, чтобы выяснить, какие модели являются детьми модели, а какие нет?
существуют ли другие подобные способы узнать, какие модели являются детьми модели?
вы можете найти дочерние элементы модели с помощью Mode._meta.related_objects
# Example Code
from django.utils.functional import classproperty
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
for rel in related_objects
if rel.related_model._meta.app_label == cls._meta.app_label
]
Вы можете использовать этот класс в качестве mro
для своих моделей.
Если вы хотите получить обратные связи с этой моделью, то лучше всего использовать ownerModel._meta.related_objects
. Однако это часть частного API Django и официально не документируется/поддерживается. Вы можете найти документацию в исходном коде.
Возвращает все связанные объекты, указывающие на текущую модель. Связанные объекты могут иметь тип связи "один к одному", "один ко многим" или "многие ко многим".
Частный API, предназначенный только для использования самим Django;
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.fields.reverse_related 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)
]
Нет очевидной причины, по которой использование __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'}
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]: {}
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_one
и one_to_many
, которые являются None
для полей без отношения и True/False
для полей с отношением:
for f in ownerModel._meta.get_fields():
if f.one_to_one or f.one_to_many:
print(f, f.on_delete)
print()