Ottieni tutti gli oggetti del modello Django correlati


88

Come posso ottenere un elenco di tutti gli oggetti del modello che hanno una ForeignKey che punta a un oggetto? (Qualcosa come la pagina di conferma dell'eliminazione nell'amministratore di Django prima di DELETE CASCADE).

Sto cercando di trovare un modo generico per unire oggetti duplicati nel database. Fondamentalmente voglio che tutti gli oggetti che hanno punti ForeignKeys per l'oggetto "B" vengano aggiornati in modo che puntino all'oggetto "A" in modo da poter eliminare "B" senza perdere nulla di importante.

Grazie per l'aiuto!


1
Vale sicuramente la pena dare un'occhiata a questo frammento di Django !
Nick Merrill

Sto cercando di implementare la stessa identica cosa da solo. Saresti disposto a condividere la tua soluzione? in particolare come ha fatto setl'oggetto correlato a indicare la A?
eugene

Risposte:


84

Django <= 1.7

Questo ti dà i nomi delle proprietà per tutti gli oggetti correlati:

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

Puoi quindi usare qualcosa di simile per ottenere tutti gli oggetti correlati:

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

Ho passato un po 'di tempo a cercare di capirlo in modo da poter implementare una sorta di "Pattern osservatore" su uno dei miei modelli. Spero sia utile.

Django 1.8+

Usa _meta.get_fields(): https://docs.djangoproject.com/en/1.10/ref/models/meta/#django.db.models.options.Options.get_fields (vedi anche il retro nella _get_fields()fonte)


7
La all()parte fallirà su un file OneToOneField. Devi rilevarlo in qualche modo.
augustomen

2
Non mostra connessioni ManyToMany ed è stato deprecato. In Django 1.8+ si consiglia di sostituirlo con _meta.get_fields(): docs.djangoproject.com/en/1.10/ref/models/meta/… (vedi anche reversenella _get_fields()fonte)
int_ua

1
Grazie per la modifica @int_ua! Sono sorpreso che questa soluzione alternativa sia rimasta compatibile con Django finché è rimasta.
rapina il

4
Seguendo il _meta.get_fields()suggerimento, questo faceva parte della soluzione per me: links = [field.get_accessor_name() for field in obj._meta.get_fields() if issubclass(type(field), ForeignObjectRel)](dato from django.db.models.fields.related import ForeignObjectRel)
driftcatcher

21

@digitalPBK era vicino ... ecco probabilmente quello che stai cercando usando il materiale integrato di Django che viene utilizzato nell'amministratore di Django per visualizzare gli oggetti correlati durante l'eliminazione

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)

questo ti consente di creare ciò che viene visualizzato dall'amministratore di Django: gli oggetti correlati da eliminare.


FWICT questo non funziona abbastanza correttamente. Sembra seguire relazioni che non implicano i criteri di cancellazione, anche se non sono sicuro del perché.
Catskul

1
Sono io o Collector(importato sulla riga 1) non viene utilizzato?
djvg

7

Fai un tentativo.

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]

Non dimenticarti anche di ManyToMany
annunciatore il

6

Quanto segue è ciò che django usa per ottenere tutti gli oggetti correlati

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

print collector.data

1
non sembra cascata. non ho fatto abbastanza test su questo particolare per conoscere le differenze complete, ma vedi la mia risposta per quello a cascata.
IMFletcher

6

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

Puoi quindi usare qualcosa di simile per ottenere tutti gli oggetti correlati:

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

Dai documenti ufficiali di Django 1.10:

MyModel._meta.get_all_related_objects () diventa:

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

Quindi, prendendo l'esempio approvato, useremmo:

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

Giusto per correggere un po 'la tua risposta 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()

Funziona per set correlati, ma non per ForeignKeys. Poiché i RelatedManagers vengono creati dinamicamente, guardare il nome della classe è più facile che eseguire 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 () è stato deprecato

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

Nota: RemovedInDjango110Warning: 'get_all_related_objects è un'API non ufficiale che è stata deprecata. Potresti essere in grado di sostituirlo con 'get_fields ()'


get_fields non restituisce oggetti correlati.
iankit

Restituisce connessioni inverse, anche su Django 1.8, vedere docs.djangoproject.com/en/1.8/_modules/django/db/models/options/…
int_ua

2

Ecco un altro modo per ottenere un elenco di campi (solo nomi) nei modelli correlati.

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

Sfortunatamente, user._meta.get_fields () restituisce solo relazioni accessibili dall'utente, tuttavia, potresti avere qualche oggetto correlato, che utilizza related_name = '+'. In tal caso, la relazione non verrebbe restituita da user._meta.get_fields (). Pertanto, se hai bisogno di un modo generico e robusto per unire oggetti, ti suggerisco di utilizzare il raccoglitore menzionato sopra.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.