web-dev-qa-db-fra.com

Comment "mettre à jour en masse" avec Django?

J'aimerais mettre à jour une table avec Django - quelque chose comme ceci dans le SQL brut:

update tbl_name set name = 'foo' where name = 'bar'

Mon premier résultat est quelque chose comme ça - mais c'est méchant, n'est-ce pas?

list = ModelClass.objects.filter(name = 'bar')
for obj in list:
    obj.name = 'foo'
    obj.save()

Y a-t-il un moyen plus élégant?

112
Thomas Schwärzl

Reportez-vous à la section suivante de la documentation Django

Mise à jour de plusieurs objets à la fois

En bref, vous devriez pouvoir utiliser: 

ModelClass.objects.filter(name='bar').update(name="foo")

Vous pouvez également utiliser des objets F pour incrémenter des lignes, par exemple:

from Django.db.models import F
Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)

Voir la documentation: https://docs.djangoproject.com/fr/1.9/topics/db/queries/

Cependant, notez que: 

  • Ceci n'utilisera pas la méthode ModelClass.save (donc si vous avez une logique à l'intérieur, elle ne sera pas déclenchée). 
  • Aucun signal Django ne sera émis.
193
jb.

Pensez à utiliser Django-bulk-update trouvé ici sur GitHub .

Installer: pip install Django-bulk-update

Implement: (code extrait directement du fichier ReadMe du projet)

from bulk_update.helper import bulk_update

random_names = ['Walter', 'The Dude', 'Donny', 'Jesus']
people = Person.objects.all()

for person in people:
    r = random.randrange(4)
    person.name = random_names[r]

bulk_update(people)  # updates all columns using the default db

Mise à jour: Comme le souligne Marc dans les commentaires, cela ne convient pas pour mettre à jour des milliers de lignes à la fois. Bien qu’il soit adapté aux plus petits lots de 10 à 100. La taille du lot qui vous convient dépend de la complexité de votre processeur et de vos requêtes. Cet outil ressemble plus à une brouette qu'à un camion à benne basculante. 

29
nu everest

La version Django 2.2 a maintenant une méthode bulk_update ( notes de version ).

https://docs.djangoproject.com/en/dev/ref/models/querysets/#bulk-update

Exemple:

# get a pk: record dictionary of existing records
updates = YourModel.objects.filter(...).in_bulk()
....
# do something with the updates dict
....
if hasattr(YourModel.objects, 'bulk_update') and updates:
    # Use the new method
    YourModel.objects.bulk_update(updates.values(), [list the fields to update], batch_size=100)
else:
    # The old & slow way
    with transaction.atomic():
        for obj in updates.values():
            obj.save(update_fields=[list the fields to update])
3
velis

Si vous souhaitez définir la même valeur sur une collection de lignes , vous pouvez utiliser la méthode update () combinée à tout terme de la requête pour mettre à jour toutes les lignes d'une requête:

some_list = ModelClass.objects.filter(some condition).values('id')
ModelClass.objects.filter(pk__in=some_list).update(foo=bar)

Si vous souhaitez mettre à jour une collection de lignes avec des valeurs différentes selon certaines conditions, vous pouvez dans le meilleur des cas regrouper les mises à jour en fonction de valeurs. Supposons que vous avez 1 000 lignes pour lesquelles vous souhaitez définir une colonne sur l'une des valeurs X, vous pouvez alors préparer les lots à l'avance, puis n'exécuter que X requêtes de mise à jour (chacune ayant essentiellement la forme du premier exemple ci-dessus) + le SELECT initial -question. 

Si chaque ligne requiert une valeur unique il n'y a aucun moyen d'éviter une requête par mise à jour. Peut-être devriez-vous vous tourner vers d'autres architectures, telles que CQRS/Event Sourcing, si vous avez besoin de performances dans ce dernier cas.

0
Andreas Bergström