user1687717
user1687717

Reputation: 3475

Create Django model or update if exists

I want to create a model object, like Person, if person's id doesn't not exist, or I will get that person object.

The code to create a new person as following:

class Person(models.Model):
    identifier = models.CharField(max_length = 10)
    name = models.CharField(max_length = 20)
    objects = PersonManager()

class PersonManager(models.Manager):
    def create_person(self, identifier):
        person = self.create(identifier = identifier)
        return person

But I don't know where to check and get the existing person object.

Upvotes: 173

Views: 253422

Answers (9)

Heraknos
Heraknos

Reputation: 393

For some reason, I needed to have the information if the object was updated (for propagation in other systems). I coded this little dirty function :

# Get object, create it if not existing, update it if existing, and return it with created and updated flag.
# pk must be in kwargs, and if creation needed, all required fields must be in kwargs
def get_or_create_or_update(classmodel, pk, **kwargs) -> (object, bool, bool):
    obj = None
    if pk not in kwargs :
        raise Exception(f"Missing pk '{pk}' in kwargs")
    try:
        obj = classmodel.objects.get(**{pk:kwargs[pk]})
    except classmodel.DoesNotExist:
        # TODO check required fields -> In fact, create will raise an error if required fields are missing
        obj = classmodel.objects.create(**kwargs)
        return obj, True, False
    
    updated = False
    for field in classmodel._meta.get_fields():
        if field.name in kwargs and getattr(obj, field.name) != kwargs[field.name] :
            setattr(obj, field.name, kwargs[field.name])
            updated = True
            
    if updated :
        obj.save()
    return obj, False, updated

Upvotes: 0

Heapify
Heapify

Reputation: 2901

You can also use update_or_create just like get_or_create and here is the pattern I follow for update_or_create assuming a model Person with id (key), name, age, is_manager as attributes -

update_values = {"is_manager": False}
new_values = {"name": "Bob", "age": 25, "is_manager":True}

obj, created = Person.objects.update_or_create(identifier='id',
                                               defaults=update_values)
if created:
    obj.update(**new_values)

Upvotes: 4

Mahbub Alam
Mahbub Alam

Reputation: 368

This should be the answer you are looking for

EmployeeInfo.objects.update_or_create(
    #id or any primary key:value to search for
    identifier=your_id, 
    #if found update with the following or save/create if not found
    defaults={'name':'your_name'}
)

Upvotes: 0

Andreas Bergström
Andreas Bergström

Reputation: 14640

For only a small amount of objects the update_or_create works well, but if you're doing over a large collection it won't scale well. update_or_create always first runs a SELECT and thereafter an UPDATE.

for the_bar in bars:
    updated_rows = SomeModel.objects.filter(bar=the_bar).update(foo=100)
        if not updated_rows:
            # if not exists, create new
            SomeModel.objects.create(bar=the_bar, foo=100)

This will at best only run the first update-query, and only if it matched zero rows run another INSERT-query. Which will greatly increase your performance if you expect most of the rows to actually be existing.

It all comes down to your use case though. If you are expecting mostly inserts then perhaps the bulk_create() command could be an option.

Upvotes: 12

bakkal
bakkal

Reputation: 55448

If you're looking for "update if exists else create" use case, please refer to @Zags excellent answer


Django already has a get_or_create, https://docs.djangoproject.com/en/dev/ref/models/querysets/#get-or-create

For you it could be :

id = 'some identifier'
person, created = Person.objects.get_or_create(identifier=id)

if created:
   # means you have created a new person
else:
   # person just refers to the existing one

Upvotes: 205

Zags
Zags

Reputation: 41338

It's unclear whether your question is asking for the get_or_create method (available from at least Django 1.3) or the update_or_create method (new in Django 1.7). It depends on how you want to update the user object.

Sample use is as follows:

# In both cases, the call will get a person object with matching
# identifier or create one if none exists; if a person is created,
# it will be created with name equal to the value in `name`.

# In this case, if the Person already exists, its existing name is preserved
person, created = Person.objects.get_or_create(
        identifier=identifier, defaults={"name": name}
)

# In this case, if the Person already exists, its name is updated
person, created = Person.objects.update_or_create(
        identifier=identifier, defaults={"name": name}
)

Upvotes: 282

Aminah Nuraini
Aminah Nuraini

Reputation: 19206

If one of the input when you create is a primary key, this will be enough:

Person.objects.get_or_create(id=1)

It will automatically update if exist since two data with the same primary key is not allowed.

Upvotes: 1

Tom Manterfield
Tom Manterfield

Reputation: 7073

Thought I'd add an answer since your question title looks like it is asking how to create or update, rather than get or create as described in the question body.

If you did want to create or update an object, the .save() method already has this behaviour by default, from the docs:

Django abstracts the need to use INSERT or UPDATE SQL statements. Specifically, when you call save(), Django follows this algorithm:

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.

It's worth noting that when they say 'if the UPDATE didn't update anything' they are essentially referring to the case where the id you gave the object doesn't already exist in the database.

Upvotes: 4

Aamir Rind
Aamir Rind

Reputation: 39689

Django has support for this, check get_or_create

person, created = Person.objects.get_or_create(name='abc')
if created:
    # A new person object created
else:
    # person object already exists

Upvotes: 11

Related Questions