50

I'm trying to update user in Django database.

Fetched data is as follows :

fetched_data = {
     'id': 1,
     'first_name': 'John',
     'last_name': 'Doe',
     'phone': '+32 12',
     'mobile_phone': '+32 13',
     'email': 'myemail@hotmail.com',
     'username': 'myusername'
}

I get the user with this id as follows :

old_user = User.objects.get(pk=fetched_data['id'])

If I update the user as follows :

old_user.username = fetched_data['username']
old_user.first_name = fetched_data['first_name']
......
old_user.save()

it works fine, but I do not want to do it for every record, thus I tried something like :

for fetched_data_key in fetched_data:
    old_user.fetched_data_key = fetched_data['fetched_data_key']
    //old_user[fetched_data_key] = fetched_data['fetched_data_key'] --- I tried this way to
    old_user.save()

But that doesn't work. Any idea how can I update the user without doing it for every record?

Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126
Boky
  • 11,554
  • 28
  • 93
  • 163

5 Answers5

67

You can update a row in the database without fetching and deserializing it; update() can do it. E.g.:

User.objects.filter(id=data['id']).update(email=data['email'], phone=data['phone'])

This will issue one SQL update statement, and is much faster than the code in your post. It will never fetch the data or waste time creating a User object.

You cannot, though, send a whole bunch of update data to the SQL database and ask it to map it to different rows in one go. If you need a massive update like that done very quickly, your best bet is probably inserting the data into a separate table and then update it form a select on that table. Django ORM does not support this, as far as I can tell.

Community
  • 1
  • 1
9000
  • 39,899
  • 9
  • 66
  • 104
  • When creating an object I know you can do something like `e = User(**data)`. Would this work for update? – DjangoBlockchain Feb 11 '20 at 14:31
  • @DjangoBlockchain: It may, but it will defeat the purpose of this answer. The idea is to _not_ create an ORM model object for each DB record you want to update. It _really_ kills performance. – 9000 Feb 11 '20 at 17:08
  • 3
    I ended up doing `User.objects.filter(id=data['id']).update(**data)`, so it just updates every field instead of specifying individual fields. Sorry if my question was confusing. – DjangoBlockchain Feb 11 '20 at 17:10
31

Even simpler if you use .update() method of QuerySet object as:

my_id = fetched_data.pop('id')  # remove 'id' from `fetch_data`
                                # as it is not supposed to be updated

User.objects.filter(pk=my_id).update(**fetched_data)
#          unpack the content of `dict` ^

It will unpack the content of fetched_data dict and will update the records in User object whose columns are present as key to the fetched_data dict. Since you are calling filter on pk, it will always return single record.

Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126
16

If you use .update then there is an issue, since it won't raise post_save or pre_save signal, so if you want to use any signal on any change of data then User.objects.filter(pk=fetched_data['id']).update(**kwargs) won't work.

So you can use setattr() to update the fields and then .save would save the row which would update the row also raise an signal.

old_user = User.objects.get(pk=fetched_data['id'])
for key, value in fetched_data.iteritems():
    setattr(old_user, key, value)
old_user.save()
Arghya Saha
  • 5,599
  • 4
  • 26
  • 48
  • 2
    This is by far the best way to go. Signals are all over the place in all medium and large Django projects; you wouldn't want to tinker with them – Chris Mar 03 '21 at 12:00
6
setattr(old_user, fetched_data_key, fetched_data['fetched_data_key'])
Alex Hall
  • 34,833
  • 5
  • 57
  • 89
-1

Blackquotes

def update_block(request, id):

a = RentedNumberBlock.objects.get(id=id)
b = RentedNumber.objects.filter(user=request.user)
c = a.rented_number.values_list('id', flat=True)
if request.POST:
    update = RentedNumberBlock.objects.get(id=id)
    update.name = request.POST['name']
    z = dict(request.POST)['rented_number']
    y = RentedNumber.objects.filter(id__in=z)
    update.rented_number.set(z)
    update.save()
    return HttpResponseRedirect('/block/')
return render(request, "block/update.html", {"a": a , "b": b, "c": c})
  • Hi and welcome to Stack Overflow! Please take the [tour](https://stackoverflow.com/tour). Thanks for contributing an answer but can you also add an explanation on how your code solves the problem? If you need help with formatting, see the [help center](https://stackoverflow.com/editing-help). – Jeanne Dark Dec 01 '20 at 07:46