Отримати всі пов’язані об’єкти моделі Django


88

Як я можу отримати список усіх модельних об'єктів, на яких ForeignKey вказує на об'єкт? (Щось на зразок сторінки підтвердження видалення в адміністраторі Django перед УДАЛИТИ КАСКАД).

Я намагаюся запропонувати загальний спосіб об'єднання повторюваних об'єктів у базі даних. В основному я хочу, щоб усі об'єкти, які мають точки ForeignKeys на об'єкт "B", були оновлені, щоб вказати на об'єкт "A", щоб я міг потім видалити "B", не втрачаючи нічого важливого.

Спасибі за вашу допомогу!


1
Цей фрагмент Django , безумовно, варто перевірити!
Nick Merrill

Я намагаюся реалізувати саме те саме. Чи хотіли б ви поділитися своїм рішенням? особливо, як 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 до тих пір, як він це робив.
грабіж

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 це працює не зовсім коректно. Здається, це слідує відносинам, які не передбачають критеріїв видалення, хоча я не впевнений, чому.
Кацкуль

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:
Nam Ngo

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

Працює для споріднених наборів, але не для ForeignKeys. Оскільки RelatedManagers створюються динамічно, переглядати назву класу простіше, ніж виконувати 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())

Примітка: RemovedInDjango110Warning: '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 = '+'. У такому випадку відношення не буде повернуте користувачем._meta.get_fields (). Тому, якщо вам потрібен загальний та надійний спосіб об’єднання об’єктів, я пропоную скористатися згаданим вище Collector.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.