from django.db import IntegrityError
def update_or_create(model, filter_kwargs, update_kwargs)
if not model.objects.filter(**filter_kwargs).update(**update_kwargs):
kwargs = filter_kwargs.copy()
kwargs.update(update_kwargs)
try:
model.objects.create(**kwargs)
except IntegrityError:
if not model.objects.filter(**filter_kwargs).update(**update_kwargs):
raise # re-raise IntegrityError
Myślę, że kod podany w pytaniu nie jest zbyt demonstracyjny: kto chce ustawić identyfikator dla modelu? Załóżmy musimy to i mamy jednoczesnych operacji:
def thread1():
update_or_create(SomeModel, {'some_unique_field':1}, {'some_field': 1})
def thread2():
update_or_create(SomeModel, {'some_unique_field':1}, {'some_field': 2})
Z update_or_create
funkcja zależy który wątek jest na pierwszym miejscu, obiekt zostanie utworzony i aktualizowany bez wyjątku. To będzie bezpieczny wątku, ale oczywiście ma niewielki wykorzystania: w zależności od rasy wartości warunku SomeModek.objects.get(some__unique_field=1).some_field
może być 1 lub 2.
Django zapewnia obiektów F, dzięki czemu możemy zaktualizować nasz kod:
from django.db.models import F
def thread1():
update_or_create(SomeModel,
{'some_unique_field':1},
{'some_field': F('some_field') + 1})
def thread2():
update_or_create(SomeModel,
{'some_unique_field':1},
{'some_field': F('some_field') + 2})
Jeśli inny proces utworzy obiekt między dwiema liniami, wywołanie create() spowoduje zgłoszenie wyjątku IntegrityError. Nie ustawiasz również identyfikatora w wywołaniu create(). – GDorn
OK, masz rację, to powinno obchodzić IntegrityError. Dokona edycji kodu. – Nik
Należy pamiętać, że to, co napisałeś powyżej, jest już w wersji dev zestawu zapytań django: https://docs.djangoproject.com/en/dev/ref/models/querysets/#update-or-create –