238 votes

Comment faire une mise à jour en masse avec Django ?

J'aimerais mettre à jour une table avec Django - quelque chose comme ceci en 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()

Existe-t-il un moyen plus élégant ?

1 votes

Vous recherchez peut-être un insert de lot. Jetez un coup d'œil à stackoverflow.com/questions/4294088/

0 votes

Je n'aime pas insérer de nouvelles données, mais seulement mettre à jour les données existantes.

3 votes

Peut-être avec l'aide de select_for_update ? docs.djangoproject.com/fr/dev/ref/models/querysets/

353voto

jb. Points 4932

Mise à jour :

La version 2.2 de Django possède désormais un mise à jour en masse .

Vieille réponse :

Consultez la section suivante de la documentation de django

Mise à jour de plusieurs objets à la fois

En bref, vous devriez être en mesure d'utiliser :

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

Vous pouvez également utiliser F pour faire des choses comme l'incrémentation des rangs :

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

Ver el documentation .

Cependant, notez que :

  • Cela n'utilisera pas ModelClass.save (ainsi, si vous avez une certaine logique à l'intérieur, elle ne sera pas déclenchée).
  • Aucun signal django ne sera émis.
  • Vous ne pouvez pas effectuer un .update() sur un QuerySet découpé en tranches, il doit l'être sur un QuerySet original ; vous devrez donc vous appuyer sur la fonction .filter() y .exclude() méthodes.

41 votes

Notez également qu'en raison de la non-utilisation de save() , DateTimeField Les champs avec auto_now=True (colonnes "modifiées") ne seront pas mises à jour.

8 votes

Mais ModelClass.objects.filter(name = 'bar').update(name="foo") ne remplit pas l'objectif de la mise à jour en masse, si j'ai des données différentes pour des identifiants différents, comment puis-je le faire sans utiliser de boucle ?

0 votes

@shihon Je ne suis pas sûr d'avoir bien compris mais j'ai ajouté un exemple à la réponse.

33voto

nu everest Points 585

Envisagez d'utiliser django-bulk-update trouvé ici sur GitHub .

Installer : pip install django-bulk-update

Mettre en œuvre : (code tiré directement du fichier ReadMe des projets)

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 Marc le souligne dans les commentaires, cette méthode n'est pas adaptée à la mise à jour de milliers de lignes à la fois. Cependant, il convient pour des lots plus petits, de 10 à 100 lignes. La taille du lot qui vous convient dépend de votre CPU et de la complexité de la requête. Cet outil ressemble plus à une brouette qu'à un camion à benne.

31voto

velis Points 641

La version 2.2 de Django possède désormais un bulk_update méthode ( notes de mise à jour ).

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

Ejemplo:

# 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])

16voto

Jasir Points 117

Voici un contenu utile que j'ai trouvé sur Internet concernant la question ci-dessus.

https://www.sankalpjonna.com/learn-django/running-a-bulk-update-with-django

La méthode inefficace

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

La méthode efficace

ModelClass.objects.filter(name = 'bar').update(name="foo") # for single value 'foo' or add loop

Utilisation de bulk_update

update_list = []
model_qs= ModelClass.objects.filter(name = 'bar')
for obj in model_qs:
    model_obj =ModelClass.object.get(id=obj.id)
    model_obj.name = "foo" # Or what ever the value is for simplicty im providing foo only
    update_list.append(model_obj)

ModelClass.objects.bulk_update(update_list,['name'])

Utilisation d'une transaction atomique

from django.db import transaction

with transaction.atomic():
    model_qs = ModelClass.objects.filter(name = 'bar')
    for obj in model_qs:
       ModelClass.objects.filter(name = 'bar').update(name="foo")

Des votes positifs ? Merci d'avance : Merci de votre attention ;)

15voto

Si vous voulez définir la même valeur sur une collection de lignes Avec la méthode update(), vous pouvez utiliser la méthode update() combinée avec n'importe quel terme de requête pour mettre à jour toutes les lignes en une seule requête :

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

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

Si chaque ligne doit avoir une valeur unique il n'y a aucun moyen d'éviter une requête par mise à jour. Si vous avez besoin de performances dans ce dernier cas, vous pouvez peut-être envisager d'autres architectures, comme CQRS/Event sourcing.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X