I'm trying to make my nested serializer to be able to process updating when I'm only updating for the parent model. The default action in drf is that the serializer cant tell if ur child model is trying to updating or create, there's a solution where you just turn off the validation, which I don't think is ideal for the database and iots implication.
I've scour about this topic for like 2 days and I think the ideal approach to this is to override the is_valid method to be able to just return the object.
Idea from :
Django REST Framework ModelSerializer get_or_create functionality
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
class MyModelSerializer(serializers.ModelSerializer):
def is_valid(self, raise_exception=False):
if hasattr(self, 'initial_data'):
# If we are instantiating with data={something}
try:
# Try to get the object in question
obj = Security.objects.get(**self.initial_data)
except (ObjectDoesNotExist, MultipleObjectsReturned):
# Except not finding the object or the data being ambiguous
# for defining it. Then validate the data as usual
return super().is_valid(raise_exception)
else:
# If the object is found add it to the serializer. Then
# validate the data as usual
self.instance = obj
return super().is_valid(raise_exception)
else:
# If the Serializer was instantiated with just an object, and no
# data={something} proceed as usual
return super().is_valid(raise_exception)
class Meta:
model = models.MyModel
The problem is whenever I try to implment I get the error:
Field 'id' expected a number but got [{'id': 18, 'title': 'aDDD', 'body': 'aDDD', 'slug': 'aDDDbabe', 'author': 1, 'question': 25}].
obj = Question.objects.get(**self.initial_data)
This code triggers it, however When I
obj = Question.answers.get(**self.initial_data)
I get 'ReverseManyToOneDescriptor' object has no attribute 'get' .
Since mine is a nested serializer I would have to find another way to make this work. Any advice where I can fix it? :(
serializers.py
from rest_framework import serializers
# https://github.com/encode/django-rest-framework/blob/3.12.4/rest_framework/serializers.py#L839
from rest_framework.fields import ( # NOQA # isort:skip
CreateOnlyDefault, CurrentUserDefault, SkipField, empty
)
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from .models import Question, Answer
class AnswerSerializer(serializers.ModelSerializer):
"""Serialize Answer model"""
class Meta:
model = Answer
fields = ('id','title', 'body', 'slug', 'author', 'question')
# https://stackoverflow.com/questions/57249850/overwriting-nested-serializers-create-method-throws-typeerror-create-got-mul
read_only_fields = ('question',)
lookup_field = 'slug'
# https://stackoverflow.com/questions/55031552/how-to-access-child-entire-record-in-parent-model-in-django-rest-framework
class QuestionSerializer(serializers.ModelSerializer):
"""Serialize Question model"""
#This answer variable and the fields 'answer' you refer to has to be the SAME but
#These can change exp: 'aaa'
answers = AnswerSerializer(read_only=False, many=True,)
class Meta:
model = Question
fields = ('id','title', 'body', 'slug', 'author', 'category', 'answers',)
lookup_field = 'slug'
def is_valid(self, raise_exception=False):
if hasattr(self, 'initial_data'):
# If we are instantiating with data={something}
try:
# Try to get the object in question
obj = Question.objects.get(**self.initial_data)
except (ObjectDoesNotExist, MultipleObjectsReturned):
print("LOLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL")
return print(obj)
else:
print("LOLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL")
print(obj)
else:
return super().is_valid(raise_exception)
def create(self, validated_data):
answers_data = validated_data.pop('answers')
question = Question.objects.create(**validated_data)
for answer_data in answers_data:
#The above stackoverflow link at the answer serializers is realted to this
Answer.objects.create(question=question, **answer_data)
return question
def update(self, instance, validated_data):
instance.title = validated_data.get('title', instance.title)
instance.body = validated_data.get('body', instance.body)
instance.slug = validated_data.get('slug', instance.slug)
instance.author = validated_data.get('author', instance.author)
instance.category = validated_data.get('category', instance.category)
instance.save()
# https://django.cowhite.com/blog/create-and-update-django-rest-framework-nested-serializers/
answers_data = validated_data.pop('answers')
aas = (instance.answers).all()
print("@@@@@@")
print(aas)
print("@@@@@@")
print(instance.answers.get().slug)
print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
aas2 = list(aas)
print("@@@@@@")
for f in validated_data:
print(f)
print(validated_data.items())
print(dir(validated_data))
print("@@@@@@")
for answer_data in answers_data:
aas3 = aas2.pop(0)
aas3.title= answer_data.get('title', aas3.title)
aas3.body= answer_data.get('body', aas3.body)
aas3.slug= answer_data.get('slug', aas3.slug)
aas3.author= answer_data.get('author', aas3.author)
aas3.question= answer_data.get('question', aas3.question)
aas3.save()
return instance
views.py
from django.shortcuts import render
from .models import Question, Answer
from django.conf import settings
from rest_framework import viewsets
from rest_framework.authentication import TokenAuthentication
from .serializers import QuestionSerializer
#from .permissions import UpdateOwnPrice
from rest_framework.permissions import IsAdminUser
class QuestionViewSet(viewsets.ModelViewSet):
"""CRUD """
serializer_class = QuestionSerializer
queryset = Question.objects.all()
authentication_classes = (TokenAuthentication,)
#permission_classes = (UpdateOwnPrice,)
lookup_field = 'slug'
extra_kwargs = {'slug': {'validators': []},}
models.py
from django.db import models
from django.conf import settings
from django.db.models import Q
from django.utils import timezone
from django.urls import reverse
class Category(models.Model):
name= models.CharField(max_length=100)
def __str__(self):
return self.name
class Question(models.Model):
title= models.CharField(max_length= 100)
body= models.TextField()
slug= models.SlugField(unique= True)
date_posted= models.DateTimeField(default=timezone.now)
author= models.ForeignKey(settings.AUTH_USER_MODEL, on_delete= models.CASCADE, related_name = 'questions')
category= models.ForeignKey(Category, on_delete= models.CASCADE, related_name = 'questions')
#objects= QnAManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('home')
class Answer(models.Model):
title= models.CharField(max_length= 100)
body= models.TextField()
slug= models.SlugField(unique= True)
date_posted= models.DateTimeField(default=timezone.now)
author= models.ForeignKey(settings.AUTH_USER_MODEL, on_delete= models.CASCADE, related_name = 'answers')
question= models.ForeignKey(Question, on_delete= models.CASCADE, related_name = 'answers')
#objects= QnAManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('home')