Django's update_or_create не работает, несмотря на указание kwargs
У меня есть следующая модель Django:
class Itens(models.Model):
id = models.AutoField(primary_key=True)
itempr_id = models.IntegerField(unique=True) # This is NOT a relationship
cod_item = models.CharField(max_length=16, unique=True)
# Other fields...
Как видите, и itempr_id
, и cod_item
уникальны, поэтому я уточнил update_and_create
, чтобы учесть это:
class CreateItensSerializer(serializers.ModelSerializer):
class Meta:
model = Itens
fields = '__all__'
def create(self, validated_data):
item, created = Itens.objects.update_or_create(
itempr_id=validated_data.get('itempr_id'),
cod_item=validated_data.get('cod_item'),
defaults=validated_data
)
return item
Я вызываю и сохраняю сериализатор следующим образом:
serializer = CreateItensSerializer(data=itens, many=True)
if serializer.is_valid():
serializer.save()
Однако всякий раз, когда возникает дубликат, я получаю следующую ошибку:
{'itempr_id': [ErrorDetail(string='itens with this itempr id already exists.', code='unique')], 'cod_item': [ErrorDetail(string='itens with this cod item already exists.', code='unique')]}
Я не ожидаю никаких проблем с нарушением уникальности, поскольку поля уже были указаны как уникальные в вызове функции
Как я могу это исправить?
EDIT: чтобы было понятно, это работает, если таблица БД пуста, нет никакой проблемы в аргументе itens
в serializer = CreateItensSerializer(data=itens, many=True)
В своем моделировании вы сделали и itempr_id
, и cod_item
уникальными, да. Но они уникальны индивидуально. Это означает, что если есть запись с itempr_id=42
, то ни одна другая строка не может иметь itempr_id=42
.
Ваш .update_or_create(…)
[Django-doc] будет обновлять строку, если оба совпадают itempr_id
и cod_item
. Но это, таким образом, нечто иное. Действительно, если мы обновляем или создаем элемент с itempr_id=42
и cod_item='A'
, и уже есть запись с itempr_id=42
, но с cod_item='B'
, она попытается создать новую запись, и, следовательно, вызовет ошибку. Если поля вместе уникальны, то есть itempr_id=42
может встречаться несколько раз, и cod_items='A'
может встречаться несколько раз, но комбинация может встречаться не более одного раза, то .update_or_create(…)
сработало бы.
Основной вопрос, вероятно, не столько технический, сколько функциональный: если два или более столбцов уникальны, и вы работаете с .update_or_create(…)
, то какую строку следует выбрать для обновления в случае столкновения? Ту, в которой уже есть itempr_id
, или ту, в которой уже есть cod_items
? Почему первое, а не второе?
Если бы мы ориентировались сначала на itempr_id
, мы могли бы работать с:
def create(self, validated_data):
updated = Itens.objects.filter(
itempr_id=validated_data.get('itempr_id')
).update(**validated_data)
if not updated:
updated = Itens.objects.filter(
cod_item=validated_data.get('cod_item'),
).update(**validated_data)
if not updated:
Item.objects.create(**validated_data)
return item
что по сути является тем же, что и .update_or_create(…)
, за исключением того, что для фильтрации используется комбинация элементов, хотя это может быть немного эффективнее.
Но если мы используем .update_or_create(…)
для простого замалчивания ошибок целостности, то это, скорее всего, вызвано плохим моделированием: не так уж часто модель содержит два или более (не первичных ключей) уникальных столбцов, если они уникальны по отдельности. Обычно уникальной должна быть комбинация.