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")
The HyperlinkedModelSerializer class is similar to the ModelSerializer class except that it uses hyperlinks to represent relationships, rather than primary keys. By default the serializer will include a url field instead of a primary key field.
The rendering process takes the intermediate representation of template and context, and turns it into the final byte stream that can be served to the client. REST framework includes a number of built in Renderer classes, that allow you to return responses with various media types.
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.
Answer posted by @Nirri helped me as well, but I've found a more elegant solution using Django's QuerySet API shortcut:
def create(self, validated_data): answer, created = Answer.objects.get_or_create( question=validated_data.get('question', None), defaults={'answer': validated_data.get('answer', None)}) return answer
It does exactly the same thing - if the Answer
to that Question
does not exist, it will be created, otherwise it is returned as-is by the question
field lookup.
This shortcut, however, won't update the object. QuerySet API has another method for an update
operation, which is called update_or_create
and posted in other answer down the thread.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With