111

I have a function which currently calls Models.object.get(), which returns either 0 or 1 model objects:

  • if it returns 0, I create a new model instance in the except DoesNotExist clause of the function.
  • Otherwise, I would like to update the fields in the pre-existing instance, without creating a new one.

I was originally attempting to call .update() on the instance which was found, but .update() seems to be only callable on a QuerySets. How do I get around changing a dozen fields, without calling .filter() and comparing the lengths to know if I have to create or update a pre-existing instance?

1
  • First you can use get_or_create, then you could show some code...
    – Jingo
    Sep 12, 2012 at 5:39

8 Answers 8

118

With the advent of Django 1.7, there is now a new update_or_create QuerySet method, which should do exactly what you want. Just be careful of potential race conditions if uniqueness is not enforced at the database level.

Example from the documentation:

obj, created = Person.objects.update_or_create(
    first_name='John', last_name='Lennon',
    defaults={'first_name': 'Bob'},
)

The update_or_create method tries to fetch an object from database based on the given kwargs. If a match is found, it updates the fields passed in the defaults dictionary.


Pre-Django 1.7:

Change the model field values as appropriate, then call .save() to persist the changes:

try:
    obj = Model.objects.get(field=value)
    obj.field = new_value
    obj.save()
except Model.DoesNotExist:
    obj = Model.objects.create(field=new_value)
# do something else with obj if need be
6
  • 5
    But suppose I have 20 fields, is there an easier way to update them? With .update() I suppose I can just pass in **kwargs?
    – zhuyxn
    Sep 12, 2012 at 5:39
  • Try looking here: stackoverflow.com/questions/1576664/… Sep 12, 2012 at 5:44
  • 10
    This approach has atomicity problems -- if two app instances both change the same instance in the DB,one could clobber the other.
    – Nils
    Oct 10, 2013 at 6:22
  • 1
    Also use this only if you work with the object. If you just want to update, it would be much better to use filter with the corresponding pk attribute. This has the advantage that only one query is issued. If you use get for an update, two queries are used.
    – Chris
    Feb 7, 2014 at 11:44
  • 1
    @zhuyxn for attr, val in kwargs.iteritems(): setattr(obj, attr, val)
    – DylanYoung
    Sep 22, 2016 at 16:33
67

if you want only to update model if exist (without create it):

Model.objects.filter(id = 223).update(field1 = 2)

mysql query:

UPDATE `model` SET `field1` = 2 WHERE `model`.`id` = 223
3
  • 9
    he is asking using .get() specifically not .filter()
    – brainLoop
    Sep 26, 2018 at 5:27
  • Also use .first() if you guys want some check-up model field before execute db query, since .filter() should using unique id as parameter Oct 17, 2022 at 19:44
  • I'm getting object has no attribute 'update'
    – AlxVallejo
    Jul 7, 2023 at 13:34
47

As of Django 1.5, there is an update_fields property on model save. eg:

obj.save(update_fields=['field1', 'field2', ...])

https://docs.djangoproject.com/en/dev/ref/models/instances/

I prefer this approach because it doesn't create an atomicity problem if you have multiple web app instances changing different parts of a model instance.

3
  • 1
    updated_fields is a list not dictionary then how we will pass the updated value for the field?
    – brainLoop
    Sep 26, 2018 at 5:34
  • 4
    @brainLoop, you wouldn't pass the values separately. You would set them on the object itself. obj.field1=value before calling save on the obj. Nov 21, 2019 at 14:20
  • 1
    update_fields argument prevents race conditions by forcing an update to the model for the specified fields. This way we avoid loading the model into memory and then saving which means that even if another application instance exists, it can't do anything in between.
    – dimyG
    Jun 2, 2020 at 9:23
30

I don't know how good or bad this is, but you can try something like this:

try:
    obj = Model.objects.get(id=some_id)
except Model.DoesNotExist:
    obj = Model.objects.create()
obj.__dict__.update(your_fields_dict) 
obj.save()
5
  • 2
    Exceptions are heavy operations and not recommended Oct 3, 2019 at 16:55
  • 6
    Exceptions are reasonable and correct. NOTE: create() in this answer should be create(id=some_id) and have its own try/except block to handle duplicate keys and/or race conditions, be run in a retry-mechanism, etc. Nov 29, 2019 at 19:30
  • 3
    Exceptions are the python way and are not heavy and are recommended. May 26, 2022 at 22:11
  • This answer is what I want.
    – bobo pei
    Nov 21, 2022 at 5:24
  • despite the way of getting the obj, obj.__dict__.update(your_fields_dict) , is the best method for me. thanks Feb 19 at 13:49
12

Here's a mixin that you can mix into any model class which gives each instance an update method:

class UpdateMixin(object):
    def update(self, **kwargs):
        if self._state.adding:
            raise self.DoesNotExist
        for field, value in kwargs.items():
            setattr(self, field, value)
        self.save(update_fields=kwargs.keys())

The self._state.adding check checks to see if the model is saved to the database, and if not, raises an error.

(Note: This update method is for when you want to update a model and you know the instance is already saved to the database, directly answering the original question. The built-in update_or_create method featured in Platinum Azure's answer already covers the other use-case.)

You would use it like this (after mixing this into your user model):

user = request.user
user.update(favorite_food="ramen")

Besides having a nicer API, another advantage to this approach is that it calls the pre_save and post_save hooks, while still avoiding atomicity issues if another process is updating the same model.

5

As @Nils mentionned, you can use the update_fields keyword argument of the save() method to manually specify the fields to update.

obj_instance = Model.objects.get(field=value)
obj_instance.field = new_value
obj_instance.field2 = new_value2

obj_instance.save(update_fields=['field', 'field2'])

The update_fields value should be a list of the fields to update as strings.

See https://docs.djangoproject.com/en/2.1/ref/models/instances/#specifying-which-fields-to-save

1

update:

1 - individual instance : get instance and update manually get() retrieve individual object

post = Post.objects.get(id=1)
post.title = "update title"
post.save()

2 - Set of instances : use update() method that works only with queryset that what would be returned by filter() method

Post.objects.filter(author='ahmed').update(title='updated title for ahmed')
0

I am using the following code in such cases:

obj, created = Model.objects.get_or_create(id=some_id)

if not created:
   resp= "It was created"
else:
   resp= "OK"
   obj.save()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.