web-dev-qa-db-fra.com

Obtenez tous les objets de modèle Django

Comment puis-je obtenir une liste de tous les objets du modèle dont la clé étrangère pointe vers un objet? (Quelque chose comme la page de confirmation de suppression dans l'administrateur Django avant DELETE CASCADE).

J'essaie de trouver une façon générique de fusionner des objets en double dans la base de données. Fondamentalement, je veux que tous les objets qui ont des points ForeignKeys vers l'objet "B" soient mis à jour pour pointer vers l'objet "A" afin que je puisse ensuite supprimer "B" sans rien perdre d'important.

Merci de votre aide!

82
erikcw

Django <= 1,7

Cela vous donne les noms de propriété pour tous les objets associés:

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

Vous pouvez ensuite utiliser quelque chose comme ça pour obtenir tous les objets associés:

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

J'ai passé un certain temps à essayer de comprendre cela afin de pouvoir implémenter une sorte de "modèle d'observateur" sur l'un de mes modèles. J'espère que c'est utile.

Django 1.8+

Utilisez _meta.get_fields(): https://docs.djangoproject.com/en/1.10/ref/models/meta/#Django.db.models.options.Options.get_fields (voir inverser dans la source _get_fields() aussi)

81
robbles

@digitalPBK était proche ... voici probablement ce que vous cherchez à utiliser les trucs intégrés de Django

from Django.db.models.deletion import Collector
from Django.contrib.admin.util import NestedObjects
collector = NestedObjects(using="default") #database name
collector.collect([objective]) #list of objects. single one won't do
print collector.data

cela vous permet de créer ce que l'administrateur Django affiche - les objets associés à supprimer.

18
IMFletcher

Essayez ceci.

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

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

Vous pouvez ensuite utiliser quelque chose comme ça pour obtenir tous les objets associés:

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

De Django 1.10 documents officiels:

MyModel._meta.get_all_related_objects () devient:

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

Donc, en prenant l'exemple approuvé, nous utiliserions:

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
6
Bojan Jovanovic

Ce qui suit est ce que Django utilise pour obtenir tous les objets associés

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

print collector.data
5
digitalPBK
for link in links:
    objects = getattr(a, link).all()

Fonctionne pour les ensembles associés, mais pas pour ForeignKeys. Puisque RelatedManagers sont créés dynamiquement, il est plus facile de regarder le nom de la classe que de faire une isinstance ()

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

Django 1.9
get_all_related_objects () a été déconseillé

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

Remarque: RemovedInDjango110Warning: 'get_all_related_objects est une API non officielle qui a été déconseillée. Vous pourrez peut-être le remplacer par 'get_fields ()'

4
Slipstream

Voici une autre façon d'obtenir une liste de champs (noms uniquement) dans les modèles associés.

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
JessieinAg

Malheureusement, user._meta.get_fields () ne renvoie que les relations accessibles depuis l'utilisateur, cependant, vous pouvez avoir un objet lié, qui utilise related_name = '+'. Dans ce cas, la relation ne serait pas retournée par user._meta.get_fields (). Par conséquent, si vous avez besoin d'un moyen générique et robuste pour fusionner des objets, je suggère d'utiliser le collecteur mentionné ci-dessus.

1
Jakub QB Dorňák