The update_or_create
model function was updated in Django 5.0 with a create_defaults
argument that allows only used during create operations.
Last active
December 13, 2023 22:52
-
-
Save myles/a6905f94e4e9955f02fea2554db982a7 to your computer and use it in GitHub Desktop.
Backwards port of Django 5.0's update_or_create function
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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