1

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')


Kentypop
  • 51
  • 7

0 Answers0