Search code examples
pythondjangodjango-rest-framework

Django Rest Framework POST Update if existing or create


I am new to DRF. I read the API docs, maybe it is obvious but I couldn't find a handy way to do it.

I have an Answer object which has one-to-one relationship with a Question.

On the frontend I used to use POST method to create an answer sent to api/answers, and PUT method to update sent to e.g. api/answers/24

But I want to handle it on the server side. I will only send a POST method to api/answers and DRF will check based on answer_id or question_id (since it is one to one) if the object exists. If it does, it will update the existing one, and if it doesn't, it will create a new answer.

I couldn't figure out where I should implement it. Should I override create() in serializer or in ViewSet or something else?

Here are my model, serializer, and view:

class Answer(models.Model):
    question = models.OneToOneField(
        Question, on_delete=models.CASCADE, related_name="answer"
    )
    answer = models.CharField(
        max_length=1, choices=ANSWER_CHOICES, null=True, blank=True
    )


class AnswerSerializer(serializers.ModelSerializer):
    question = serializers.PrimaryKeyRelatedField(
        many=False, queryset=Question.objects.all()
    )

    class Meta:
        model = Answer
        fields = ("id", "answer", "question")


class AnswerViewSet(ModelViewSet):
    queryset = Answer.objects.all()
    serializer_class = AnswerSerializer
    filter_fields = ("question", "answer")

Solution

  • Unfortunately your provided and accepted answer does not answer your original question, since it does not update the model. This however is easily achieved by another convenience method: update-or-create

    def create(self, validated_data):
        answer, created = Answer.objects.update_or_create(
            question=validated_data.get('question', None),
            defaults={'answer': validated_data.get('answer', None)})
        return answer
    

    This should create an Answer object in the database if one with question=validated_data['question'] does not exist with the answer taken from validated_data['answer']. If it already exists, django will set its answer attribute to validated_data['answer'].

    As noted by the answer of Nirri, this function should reside inside the serializer. If you use the generic ListCreateView it will call the create function once a post request is sent and generate the corresponding response.