首页 > 解决方案 > Django 中主键的重复条目 - 我如何在 Django 中进行 upsert?

问题描述

我正在尝试在 Django 上执行 update_or_create,其中还涉及一些逻辑(由于我使用的字符集的独特性,我必须与 PUT 的最佳实践略有偏差)。

我的视图中有以下代码(仅显示 PUT 方法):

class WordView(APIView):

    def put(self, request, user_id):
        character = request.data.get('character')
        correct = request.data.get('correct')
        mistakes = request.data.get('mistakes')
        score = request.data.get('score')

        if Word.objects.filter(user_id=user_id, character=character).exists():
            orig_word = Word.objects.get(user_id=user_id, character=character)
            correct = correct + orig_word.correct
            mistakes = mistakes + orig_word.mistakes
            trials = orig_word.trials + 1
            score = (score + (orig_word.score * orig_word.trials)) / trials
            pk = orig_word.pk
        else:
            trials = 1

        # Note - I know that pk won't exist on create - just testing update right now

        word_saves = {"user_id": user_id, "character": character,
                      "correct": correct, "mistakes": mistakes, "score": score, "trials": trials, "pk": pk}

        serializer = WordSerializer(data=word_saves)
        if serializer.is_valid(raise_exception=True):
            word_saved = serializer.save()

        return Response({"success": "Word character {} saved for user {}".format(word_saved.character, word_saved.user_id)})

然后在我的 WordSerializer 中,我有这个:

class WordSerializer(serializers.Serializer):
    user_id = serializers.IntegerField()
    character = serializers.CharField()
    correct = serializers.IntegerField()
    mistakes = serializers.IntegerField()
    score = serializers.FloatField()
    trials = serializers.IntegerField()
    pk = serializers.IntegerField()

    def create(self, validated_data):
        return Word.objects.update_or_create(**validated_data)

但是,当我在存在具有给定 user_id 和字符的行时发出 PUT 请求时,我得到以下信息:

django.db.utils.IntegrityError: (1062, "Duplicate entry '29' for key 'PRIMARY'")

问题是,我用我的新值更新数据库中存在的值。我认为那是什么update_or_create- 一个upsert。

如何使用我目前拥有的代码进行更新插入?

标签: django

解决方案


您应该使用defaults关键字参数在调用时传递要更新的字段update_or_create。您传递的关键字参数defaults将用于匹配您要更新的现有记录。

在您的情况下,如果user_id并且character是模型的唯一标识字段,您应该将这些字段作为关键字传递

class WordSerializer(serializers.Serializer):

    def create(self, validated_data):
        return Word.objects.update_or_create(
            user_id=validated_data.pop('user_id'),
            character=validated_data.pop('character'),
            defaults=validated_data
        )

推荐阅读