Let's say I have a list of dicts with emails and birthdays:

p = [\
    {'email': 'someone@somewhere.com', 'birthday': '1990-01-01'},
    {'email': 'someone_else@somewhereelse.com', 'birthday': '1980-02-05'},
    #...etc
]

I want to update the People objects in the database. The raw sql would normally have as many queries as len(p), so this makes me think of doing:

from django.db import IntegrityError, transaction
for person in p:
    try:
        with transaction.atomic():
            People.objects.filter(email=person['email']).update(birthday=person['birthday'])
    except IntegrityError:
        #handle the exception

I'm not experienced enough in Django to know if this is good practice though. What's the most pythonic way to express what I'm trying to do?

score:3

Accepted answer

When you filter and update, the same update will be applied to all the filtered entries. Django doesn't provide a bulk update operation where you can update different values on different entries.

You can make your code better by including the for loop inside the transaction.atomic() block instead of having the atomic block for each entry in the for loop:

with transaction.atomic():
    for person in p:
        try:              
            People.objects.filter(email=person['email']).update(birthday=person['birthday'])
        except IntegrityError:
            #handle the exception

score:3

There is a library: django-bulk-update.

people = Person.objects.all()
for person in people:
  person.name = random.choice(random_names)

bulk_update(people, update_fields=['name']) 
# or 
Person.objects.bulk_update(people, update_fields=['name']) 

score:5

Using Django's bulk_update - https://docs.djangoproject.com/en/3.1/ref/models/querysets/#bulk-update

objs = []
for person in p:
    obj = People.objects.get(email=person['email'])
    obj.birthday = person['birthday']
    objs.append(obj)
People.objects.bulk_update(objs, ['birthday'], batch_size=1000)

Related Query