Dapatkan semua objek model Django terkait

88

Bagaimana saya bisa mendapatkan daftar semua objek model yang memiliki ForeignKey yang menunjuk ke suatu objek? (Sesuatu seperti halaman konfirmasi hapus di admin Django sebelum DELETE CASCADE).

Saya mencoba menemukan cara umum untuk menggabungkan objek duplikat dalam database. Pada dasarnya saya ingin semua objek yang memiliki poin ForeignKeys ke objek "B" diperbarui agar mengarah ke objek "A" sehingga saya dapat menghapus "B" tanpa kehilangan sesuatu yang penting.

Terima kasih atas bantuan Anda!

erikcw
sumber
1
Ini potongan Django pasti senilai check-out!
Nick Merrill
Saya sendiri mencoba menerapkan hal yang sama persis. Apakah Anda bersedia membagikan solusi Anda? terutama bagaimana setobjek terkait mengarah ke A?
eugene

Jawaban:

84

Django <= 1.7

Ini memberi Anda nama properti untuk semua objek terkait:

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

Anda kemudian dapat menggunakan sesuatu seperti ini untuk mendapatkan semua objek terkait:

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

Saya menghabiskan waktu untuk mencoba mencari tahu hal ini sehingga saya dapat menerapkan semacam "Pola Pengamat" pada salah satu model saya. Semoga bermanfaat.

Django 1.8+

Gunakan _meta.get_fields(): https://docs.djangoproject.com/en/1.10/ref/models/meta/#django.db.models.options.Options.get_fields (lihat juga sebaliknya di _get_fields()sumber)

robbles
sumber
7
Bagian tersebut all()akan gagal pada a OneToOneField. Anda harus mendeteksinya entah bagaimana.
augustomen
2
Itu tidak menunjukkan koneksi ManyToMany dan tidak digunakan lagi. Dalam Django 1.8+ direkomendasikan untuk diganti dengan _meta.get_fields(): docs.djangoproject.com/en/1.10/ref/models/meta/… (lihat juga reversedi _get_fields()sumbernya)
int_ua
1
Terima kasih telah mengedit @int_ua! Saya terkejut solusi ini tetap kompatibel dengan Django selama itu terjadi.
merampok
4
Mengikuti _meta.get_fields()tip, ini adalah bagian dari solusi untuk saya: links = [field.get_accessor_name() for field in obj._meta.get_fields() if issubclass(type(field), ForeignObjectRel)](diberikan from django.db.models.fields.related import ForeignObjectRel)
driftcatcher
21

@digitalPBK sudah dekat ... di sini mungkin yang anda cari untuk menggunakan barang bawaan Django yang digunakan dalam admin Django untuk menampilkan obyek terkait selama penghapusan

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)

ini memperbolehkan anda untuk membuat apa yang ditampilkan oleh admin Django - obyek terkait yang akan dihapus.

IMFletcher
sumber
FWICT ini tidak bekerja dengan benar. Tampaknya mengikuti hubungan yang tidak menyiratkan kriteria penghapusan, meskipun saya tidak yakin mengapa.
Catskul
1
Apakah saya, atau Collector(diimpor pada baris 1) tidak digunakan?
djvg
7

Cobalah ini.

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
sumber
Jangan lupa tentang ManyToMany juga
theannouncer
6

Berikut ini adalah apa yang django gunakan untuk mendapatkan semua objek terkait

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

print collector.data
digitalPBK
sumber
1
sepertinya tidak mengalir. belum cukup melakukan pengujian di sekitar partikel ini untuk mengetahui perbedaan lengkapnya, tetapi lihat jawaban saya untuk yang berjenjang.
IMFletcher
6

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

Anda kemudian dapat menggunakan sesuatu seperti ini untuk mendapatkan semua objek terkait:

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

Dari dokumen resmi Django 1.10:

MyModel._meta.get_all_related_objects () menjadi:

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

Jadi dengan mengambil contoh yang disetujui kami akan menggunakan:

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
sumber
Hanya untuk mengoreksi jawaban Anda sedikit 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()

Berfungsi untuk set terkait, tetapi tidak untuk ForeignKeys. Karena RelatedManagers dibuat secara dinamis, melihat nama kelas lebih mudah daripada melakukan isinstance ()

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

Django 1.9
get_all_related_objects () tidak digunakan lagi

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

Catatan: RemovedInDjango110Warning: 'get_all_related_objects adalah API tidak resmi yang sudah tidak digunakan lagi. Anda mungkin bisa menggantinya dengan 'get_fields ()'

Slipstream
sumber
get_fields tidak mengembalikan objek terkait.
iankit
Ia mengembalikan koneksi terbalik, bahkan pada Django 1.8, lihat docs.djangoproject.com/en/1.8/_modules/django/db/models/options/…
int_ua
2

Berikut adalah cara lain untuk mendapatkan daftar bidang (hanya nama) dalam model terkait.

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
sumber
2

Sayangnya, user._meta.get_fields () hanya mengembalikan relasi yang dapat diakses dari pengguna, namun, Anda mungkin memiliki beberapa objek terkait, yang menggunakan related_name = '+'. Dalam kasus seperti itu, relasi tidak akan dikembalikan oleh user._meta.get_fields (). Oleh karena itu, jika Anda membutuhkan cara umum dan kuat untuk menggabungkan objek, saya sarankan untuk menggunakan Collector yang disebutkan di atas.

Jakub QB Dorňák
sumber