Holen Sie sich alle verwandten Django-Modellobjekte

86

Wie kann ich eine Liste aller Modellobjekte abrufen, bei denen ein ForeignKey auf ein Objekt verweist? (So ​​etwas wie die Löschbestätigungsseite im Django-Administrator vor DELETE CASCADE).

Ich versuche, eine generische Methode zum Zusammenführen doppelter Objekte in der Datenbank zu finden. Grundsätzlich möchte ich, dass alle Objekte, die ForeignKeys-Punkte auf Objekt "B" haben, aktualisiert werden, um auf Objekt "A" zu zeigen, damit ich dann "B" löschen kann, ohne etwas Wichtiges zu verlieren.

Danke für Ihre Hilfe!

erikcw
quelle
1
Dieses Django-Snippet ist definitiv einen Besuch wert!
Nick Merrill
Ich versuche genau das Gleiche selbst umzusetzen. Wären Sie bereit, Ihre Lösung zu teilen? Insbesondere, wie hat setdas zugehörige Objekt auf das A gezeigt?
Eugene

Antworten:

83

Django <= 1,7

Dadurch erhalten Sie die Eigenschaftsnamen für alle verwandten Objekte:

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

Sie können dann so etwas verwenden, um alle zugehörigen Objekte abzurufen:

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

Ich habe eine Weile versucht, dies herauszufinden, damit ich eine Art "Beobachtermuster" auf einem meiner Modelle implementieren kann. Hoffe es ist hilfreich.

Django 1.8+

Verwenden Sie _meta.get_fields(): https://docs.djangoproject.com/de/1.10/ref/models/meta/#django.db.models.options.Options.get_fields (siehe auch in der _get_fields()Quelle umgekehrt )

raubt
quelle
7
Das all()Teil wird auf einem fehlschlagen OneToOneField. Man muss es irgendwie erkennen.
August
2
ManyToMany-Verbindungen werden nicht angezeigt und sind veraltet. In Django 1.8+ wird empfohlen, es zu ersetzen durch _meta.get_fields(): docs.djangoproject.com/de/1.10/ref/models/meta/… (siehe auch reversein der _get_fields()Quelle)
int_ua
1
Danke für die Bearbeitung @int_ua! Ich bin überrascht, dass diese Problemumgehung so lange mit Django kompatibel blieb.
raubt
4
Dem _meta.get_fields()Tipp folgend , war dies Teil der Lösung für mich: links = [field.get_accessor_name() for field in obj._meta.get_fields() if issubclass(type(field), ForeignObjectRel)](gegeben from django.db.models.fields.related import ForeignObjectRel)
Driftcatcher
20

@digitalPBK war in der Nähe ... hier ist wahrscheinlich das, wonach Sie suchen, um Djangos integriertes Material zu verwenden, das in Django admin zum Anzeigen verwandter Objekte während des Löschens verwendet wird

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)

Auf diese Weise können Sie erstellen, was der Django-Administrator anzeigt - die zu löschenden zugehörigen Objekte.

IMFletcher
quelle
FWICT das funktioniert nicht ganz richtig. Es scheint Beziehungen zu folgen, die die Löschkriterien nicht implizieren, obwohl ich nicht sicher bin, warum.
Catskul
1
Bin ich es oder wird das Collector(in Zeile 1 importiert) nicht verwendet?
DJVG
7

Probieren Sie es aus.

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]
Buckley
quelle
Vergessen Sie auch nicht ManyToMany
Ansager
6

Das Folgende wird von Django verwendet, um alle verwandten Objekte abzurufen

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

print collector.data
digitalPBK
quelle
1
scheint nicht zu kaskadieren. Ich habe nicht genug Tests um dieses spezielle durchgeführt, um die vollständigen Unterschiede zu kennen, aber siehe meine Antwort für das kaskadierende.
IMFletcher
6

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

Sie können dann so etwas verwenden, um alle zugehörigen Objekte abzurufen:

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

Aus Django 1.10 offiziellen Dokumenten:

MyModel._meta.get_all_related_objects () wird:

[
    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
]

Wenn wir also das genehmigte Beispiel nehmen, würden wir verwenden:

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
Bojan Jovanovic
quelle
Nur um Ihre Antwort ein wenig zu korrigieren 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()

Funktioniert für verwandte Sets, jedoch nicht für ForeignKeys. Da RelatedManager dynamisch erstellt werden, ist das Anzeigen des Klassennamens einfacher als das Ausführen einer isinstance ()

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

Django 1.9
get_all_related_objects () ist veraltet

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

Hinweis: RemovedInDjango110Warning: 'get_all_related_objects ist eine inoffizielle API, die veraltet ist. Möglicherweise können Sie es durch 'get_fields ()' ersetzen.

Windschatten
quelle
get_fields gibt keine verwandten Objekte zurück.
Iankit
Es gibt auch bei Django 1.8 umgekehrte Verbindungen zurück, siehe docs.djangoproject.com/de/1.8/_modules/django/db/models/options/…
int_ua
2

Hier ist eine andere Möglichkeit, eine Liste von Feldern (nur Namen) in verwandten Modellen abzurufen.

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
JessieinAg
quelle
2

Leider gibt user._meta.get_fields () nur Beziehungen zurück, auf die der Benutzer zugreifen kann. Möglicherweise haben Sie jedoch ein verwandtes Objekt, das related_name = '+' verwendet. In diesem Fall würde die Beziehung von user._meta.get_fields () nicht zurückgegeben. Wenn Sie eine generische und robuste Methode zum Zusammenführen von Objekten benötigen, würde ich empfehlen, den oben genannten Collector zu verwenden.

Jakub QB Dorňák
quelle