Is there an idiomatic way to get_or_create and then update an object in Django?

I have a Django model called StaffSettings that contains various configuration options for users in my Django application. Each user has no more than one entry in the StaffSettings table.

Suppose one parameter has a default value of year_level, and I have code for my custom objects, for example:

def set_default_year_level(u, year_level): obj, _created = StaffSettings.objects.get_or_create(user=u) obj.default_year_level = year_level obj.save() 

I would prefer the function body to be placed on one line, because this seems like a normal use case, but if I defined it as

 def set_default_year_level(u, year_level): StaffSettings.objects.filter(user=u).update(default_year_level=year_level) 

which works fine if the user who has the question has a row in the StaffSettings table, but it will not create the corresponding row if it does not exist.

What is the idiomatic / best way to code this? (for example, is there any filter_or_create function? Or do other people write decorators / helper functions to handle this idiom?)

+7
source share
2 answers

I do not see problems with your first function, I would write the same for this utility.

However, if you need the same function in many fields on your model, and you do not want to repeat yourself, you can pass the field as a parameter:

 def set_default_value(u, field, value): obj, _created = StaffSettings.objects.get_or_create(user=u) setattr(obj, field, value) obj.save() 

And in any case, I will stay away from the update () function, since this function is designed to update several objects at the same time and does not start the save () method and signals on your models (see https://docs.djangoproject.com/en/ dev / topics / db / queries / # updating-multiple-objects-at-once )

+5
source

With Django 1.7 you can use update_or_create() :

 def set_default_year_level(u, year_level): obj, _created = StaffSettings.objects.update_or_create( user=u, default_year_level=year_level ) 

Or, for a more general case, as described in the previous answer:

 def set_default_values(u, **kwargs): obj, _created = StaffSettings.objects.update_or_create(user=u, defaults=kwargs) 

which also reaches your additional requirement

I would prefer the function body to fit on one line

+2
source

All Articles