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(…) для простого замалчивания ошибок целостности, то это, скорее всего, вызвано плохим моделированием: не так уж часто модель содержит два или более (не первичных ключей) уникальных столбцов, если они уникальны по отдельности. Обычно уникальной должна быть комбинация.

Вернуться на верх