Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: save() vs update() to update the database?

Tags:

django

I'm writing a Django app, and I need a function to update a field in the database. Is there any reason to do one of these methods rather than the other?

def save_db_field(name,field,value):     obj = MyModel.objects.get(name=name)     obj.field = value     obj.save()  def update_db_field(name,field,value):     MyModel.objects.get(name=name).update(field=value) 

It seems like the second is better because it does it in one DB call instead of two. Is there a reason why fetching, then updating is any better?

like image 852
zoidberg Avatar asked May 26 '15 05:05

zoidberg


People also ask

What does save () do in Django?

To save changes to an object that's already in the database, use save() . This performs an UPDATE SQL statement behind the scenes. Django doesn't hit the database until you explicitly call save() .

How Django knows to update VS INSERT?

The doc says: If the object's primary key attribute is set to a value that evaluates to True (i.e. a value other than None or the empty string), Django executes an UPDATE. If the object's primary key attribute is not set or if the UPDATE didn't update anything, Django executes an INSERT link.

How do I update a field in Django?

Use update_fields in save() If you would like to explicitly mention only those columns that you want to be updated, you can do so using the update_fields parameter while calling the save() method. You can also choose to update multiple columns by passing more field names in the update_fields list.


1 Answers

There are several key differences.

update is used on a queryset, so it is possible to update multiple objects at once.

As @FallenAngel pointed out, there are differences in how custom save() method triggers, but it is also important to keep in mind signals and ModelManagers. I have build a small testing app to show some valuable differencies. I am using Python 2.7.5, Django==1.7.7 and SQLite, note that the final SQLs may vary on different versions of Django and different database engines.

Ok, here's the example code.

models.py:

from __future__ import print_function from django.db import models from django.db.models import signals from django.db.models.signals import pre_save, post_save from django.dispatch import receiver  __author__ = 'sobolevn'  class CustomManager(models.Manager):     def get_queryset(self):         super_query = super(models.Manager, self).get_queryset()         print('Manager is called', super_query)         return super_query   class ExtraObject(models.Model):     name = models.CharField(max_length=30)      def __unicode__(self):         return self.name   class TestModel(models.Model):      name = models.CharField(max_length=30)     key = models.ForeignKey('ExtraObject')     many = models.ManyToManyField('ExtraObject', related_name='extras')      objects = CustomManager()      def save(self, *args, **kwargs):         print('save() is called.')         super(TestModel, self).save(*args, **kwargs)      def __unicode__(self):         # Never do such things (access by foreing key) in real life,         # because it hits the database.         return u'{} {} {}'.format(self.name, self.key.name, self.many.count())   @receiver(pre_save, sender=TestModel) @receiver(post_save, sender=TestModel) def reicever(*args, **kwargs):     print('signal dispatched') 

views.py:

def index(request):     if request and request.method == 'GET':          from models import ExtraObject, TestModel          # Create exmple data if table is empty:         if TestModel.objects.count() == 0:             for i in range(15):                 extra = ExtraObject.objects.create(name=str(i))                 test = TestModel.objects.create(key=extra, name='test_%d' % i)                 test.many.add(test)                 print test          to_edit = TestModel.objects.get(id=1)         to_edit.name = 'edited_test'         to_edit.key = ExtraObject.objects.create(name='new_for')         to_edit.save()          new_key = ExtraObject.objects.create(name='new_for_update')         to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key)         # return any kind of HttpResponse 

That resuled in these SQL queries:

# to_edit = TestModel.objects.get(id=1): QUERY = u'SELECT "main_testmodel"."id", "main_testmodel"."name", "main_testmodel"."key_id"  FROM "main_testmodel"  WHERE "main_testmodel"."id" = %s LIMIT 21'  - PARAMS = (u'1',)  # to_edit.save(): QUERY = u'UPDATE "main_testmodel" SET "name" = %s, "key_id" = %s  WHERE "main_testmodel"."id" = %s'  - PARAMS = (u"'edited_test'", u'2', u'1')  # to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key): QUERY = u'UPDATE "main_testmodel" SET "name" = %s, "key_id" = %s  WHERE "main_testmodel"."id" = %s'  - PARAMS = (u"'updated_name'", u'3', u'2') 

We have just one query for update() and two for save().

Next, lets talk about overriding save() method. It is called only once for save() method obviously. It is worth mentioning, that .objects.create() also calls save() method.

But update() does not call save() on models. And if no save() method is called for update(), so the signals are not triggered either. Output:

Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.  # TestModel.objects.get(id=1): Manager is called [<TestModel: edited_test new_for 0>] Manager is called [<TestModel: edited_test new_for 0>] save() is called. signal dispatched signal dispatched  # to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key): Manager is called [<TestModel: edited_test new_for 0>] 

As you can see save() triggers Manager's get_queryset() twice. When update() only once.

Resolution. If you need to "silently" update your values, without save() been called - use update. Usecases: last_seen user's field. When you need to update your model properly use save().

like image 195
sobolevn Avatar answered Sep 19 '22 13:09

sobolevn