Skip to content

Instantly share code, notes, and snippets.

@myles
Last active December 13, 2023 22:52
Show Gist options
  • Save myles/a6905f94e4e9955f02fea2554db982a7 to your computer and use it in GitHub Desktop.
Save myles/a6905f94e4e9955f02fea2554db982a7 to your computer and use it in GitHub Desktop.
Backwards port of Django 5.0's update_or_create function

Backwards port of Django's 5.0 update_or_create

The update_or_create model function was updated in Django 5.0 with a create_defaults argument that allows only used during create operations.

import typing as t
from django.db.models import Model
def update_or_create(
_model: t.Type[Model],
*,
defaults: t.Dict[str, t.Any] = None,
create_defaults: t.Dict[str, t.Any] = None,
**kwargs,
) -> t.Tuple[Model, bool]:
'''
Update or create a model object.
- You are probably wondering why do we need this when
Model.objects.update_or_create exists? Well, this one as an extra
argument, `create_defaults`, which allows us to set the default values
for a model object when creating it. This was actually introduce in
Django 5.0, but we are not there yet.
'''
defaults = defaults or {}
create_defaults = create_defaults or {}
try:
model_obj = _model.objects.get(**kwargs)
for key, value in defaults.items():
setattr(model_obj, key, value)
model_obj.save()
created = False
except _model.DoesNotExist:
new_values = {**defaults, **create_defaults, **kwargs}
model_obj = _model(**new_values)
model_obj.save()
created = True
return model_obj, created
from model_bakery import baker
import pytest
from django.contib.auth.models import User
@pytest.mark.django_db
def test_update_or_create__update():
user = baker.make(
User,
first_name='Fin',
last_name='the Human',
email='finn@treehouse.ooo',
)
expected_user_id = user.pk
expected_first_name = 'Finn'
expected_last_name = user.last_name
expected_email = user.email
user, user_created = db.update_or_create(
_model=User,
defaults={'first_name': expected_first_name},
create_defaults={'last_name': 'the kho-mein'},
email=user.email,
)
assert user.pk == expected_user_id
assert user.first_name == expected_first_name
assert user.last_name == expected_last_name
assert user.email == expected_email
assert user_created is False
@pytest.mark.django_db
def test_update_or_create__created():
expected_first_name = 'Jake'
expected_last_name = 'the Dog'
expected_email = 'jake@treehouse.ooo'
assert User.objects.filter(email=expected_email).count() == 0
user, user_created = db.update_or_create(
_model=User,
defaults={'first_name': expected_first_name},
create_defaults={'last_name': expected_last_name},
email=expected_email,
)
assert user.email == expected_email
assert user.first_name == expected_first_name
assert user.last_name == expected_last_name
assert user_created is True
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment