Получить все связанные объекты модели Django


88

Как я могу получить список всех объектов модели, у которых ForeignKey указывает на объект? (Что-то вроде страницы подтверждения удаления в админке Django перед КАСКАДОМ УДАЛЕНИЯ).

Я пытаюсь найти общий способ объединения повторяющихся объектов в базе данных. По сути, я хочу, чтобы все объекты, у которых ForeignKeys указывает на объект «B», были обновлены, чтобы они указывали на объект «A», чтобы затем я мог удалить «B», не теряя ничего важного.

Спасибо за вашу помощь!


1
Этот фрагмент Django определенно стоит проверить!
Ник Меррилл

Я сам пытаюсь реализовать то же самое. Вы бы хотели поделиться своим решением? особенно как setсвязанный объект указывал на букву А?
Евгений

Ответы:


84

Джанго <= 1,7

Это дает вам имена свойств для всех связанных объектов:

links = [rel.get_accessor_name() for rel in a._meta.get_all_related_objects()]

Затем вы можете использовать что-то вроде этого, чтобы получить все связанные объекты:

for link in links:
    objects = getattr(a, link).all()
    for object in objects:
        # do something with related object instance

Я потратил некоторое время, пытаясь понять это, чтобы реализовать своего рода «паттерн наблюдателя» на одной из моих моделей. Надеюсь, это поможет.

Django 1.8+

Используйте _meta.get_fields(): https://docs.djangoproject.com/en/1.10/ref/models/meta/#django.db.models.options.Options.get_fields (см. Также обратную сторону _get_fields()источника)


7
all()Часть потерпит неудачу на OneToOneField. Вы должны как-то это обнаружить.
август

2
Он не показывает подключения ManyToMany и устарел. В Django 1.8+ рекомендуется заменить на _meta.get_fields(): docs.djangoproject.com/en/1.10/ref/models/meta/… (см. Также reverseв _get_fields()источнике)
int_ua

1
Спасибо за редактирование @int_ua! Я удивлен, что это обходное решение оставалось совместимым с Django до тех пор, пока это было.
robbles

4
Следуя _meta.get_fields()подсказке, это было для меня частью решения: links = [field.get_accessor_name() for field in obj._meta.get_fields() if issubclass(type(field), ForeignObjectRel)](дано from django.db.models.fields.related import ForeignObjectRel)
driftcatcher

20

@digitalPBK был близок ... вот, вероятно, то, что вы ищете, используя встроенный материал Django, который используется в администраторе Django для отображения связанных объектов во время удаления

from django.contrib.admin.utils import NestedObjects
collector = NestedObjects(using="default") #database name
collector.collect([objective]) #list of objects. single one won't do
print(collector.data)

это позволяет вам создавать то, что отображает администратор Django - связанные объекты, которые нужно удалить.


FWICT это работает не совсем правильно. Похоже, что он соответствует отношениям, которые не подразумевают критериев удаления, хотя я не уверен, почему.
Catskul

1
Это я или Collector(импортированный в строку 1) не используется?
djvg

7

Попробуйте.

class A(models.Model):
    def get_foreign_fields(self):
      return [getattr(self, f.name) for f in self._meta.fields if type(f) == models.fields.related.ForeignKey]

Не забывайте и о ManyToMany
диктор

6

Ниже приводится то, что django использует для получения всех связанных объектов.

from django.db.models.deletion import Collector
collector = Collector(using="default")
collector.collect([a])

print collector.data

1
не кажется, что каскад. не провели достаточного тестирования этого конкретного, чтобы узнать полные различия, но посмотрите мой ответ на каскадный.
IMFletcher

6

links = [rel.get_accessor_name() for rel in a._meta.get_all_related_objects()]

Затем вы можете использовать что-то вроде этого, чтобы получить все связанные объекты:

for link in links:
    objects = getattr(a, link.name).all()
    for object in objects:
        # do something with related object instance

Из официальных документов Django 1.10:

MyModel._meta.get_all_related_objects () становится:

[
    f for f in MyModel._meta.get_fields()
    if (f.one_to_many or f.one_to_one)
    and f.auto_created and not f.concrete
]

Итак, взяв утвержденный пример, мы будем использовать:

links = [
            f for f in MyModel._meta.get_fields()
            if (f.one_to_many or f.one_to_one)
            and f.auto_created and not f.concrete
        ]

for link in links:
    objects = getattr(a, link.name).all()
    for object in objects:
        # do something with related object instance

Просто чтобы немного поправить свой ответ python for link in links: objects = getattr(a, link.name).all() for object in objects:
Нам Нго

5
for link in links:
    objects = getattr(a, link).all()

Работает для связанных наборов, но не для ForeignKeys. Поскольку связанные менеджеры создаются динамически, смотреть на имя класса проще, чем выполнять isinstance ()

objOrMgr = getattr(a, link)
 if objOrMgr.__class__.__name__ ==  'RelatedManager':
      objects = objOrMgr.all()
 else:
      objects = [ objOrMgr ]
 for object in objects:
      # Do Stuff

4

Django 1.9
get_all_related_objects () устарел

#Example: 
user = User.objects.get(id=1)
print(user._meta.get_fields())

Примечание. RemovedInDjango110Предупреждение: get_all_related_objects - это неофициальный API, который устарел. Вы можете заменить его на get_fields ()


get_fields не возвращает связанные объекты.
iankit

Он действительно возвращает обратные соединения, даже на Django 1.8, см. Docs.djangoproject.com/en/1.8/_modules/django/db/models/options/…
int_ua

2

Вот еще один способ получить список полей (только имена) в связанных моделях.

def get_related_model_fields(model):
    fields=[]
    related_models = model._meta.get_all_related_objects()
    for r in related_models:
        fields.extend(r.opts.get_all_field_names())
    return fields

2

К сожалению, user._meta.get_fields () возвращает только отношения, доступные пользователю, однако у вас может быть какой-то связанный объект, который использует related_name = '+'. В таком случае отношение не будет возвращено user._meta.get_fields (). Поэтому, если вам нужен универсальный и надежный способ слияния объектов, я бы предложил использовать упомянутый выше Collector.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.