0

I have multiple choice nested serializer, I am able to get the value but during patch call subjects not getting updated

Here User and Subject are two models,

**Model.py**

class Subject(models.Model):
    uid = models.AutoField(verbose_name='ID', 
                                  serialize=False, 
                                  auto_created=True, 
                                  primary_key=True)
    ENG = "ENGLISH"
    HND = "HINDI"

    SUBJECT = (
        (ENG, "English"),
        (HND, "Hindi"),
    )

    subject = models.CharField(
        max_length=50, choices=SUBJECT, default=ENG)

    def __str__(self):
        return self.subject


class User(AbstractUser):
    uid = models.AutoField(verbose_name='ID', 
                                  serialize=False, 
                                  auto_created=True, 
                                  primary_key=True)
    TEACHER = "Teacher"
    STUDENT = "Student"

    user_type = models.CharField(max_length=30, default=STUDENT)
    approved = models.BooleanField(default=True)

    def save(self, *args, **kwargs):
        if self.user_type == User.TEACHER and self._state.adding:
            self.approved = False
        super().save(*args, **kwargs)

    @property
    def syllabus(self):
        ret = self.teacher.syllabus_set.all()
        if ret:
            return ret
        else:
            return ''

Here is my serializer call


**serializers.py**

class TeacherProfileDetails(serializers.ModelSerializer):
    logger = logging.getLogger(__name__)
    teacher_date = AvailabilityDetails(many=True, read_only=True)
    first_name = serializers.CharField(source='user.first_name', read_only=True)
    last_name = serializers.CharField(source='user.last_name', read_only=True)
    cities = CitySerializer(many=True, read_only=True)
    subject = serializers.SerializerMethodField()
    user = UserDetailsSerializer(read_only=True)

    class Meta:
        model = Teacher
        fields = ('user', 'first_name', 'last_name', 
                  'bio', 'teacher_cost', 'subject', 'teacher_date', 'cities')

    def get_subject(self, obj):
        subject_list = []
        for i in obj.subject.all():
            subject_list.append(i.subject)
        return subject_list

Here is my views.py call

**views.py**

class TeacherListCreateAPIView(APIView):
        logger = logging.getLogger(__name__)

        #def create(self, request, *args, **kwargs):
        def get(self, request, *args, **kwargs):
            self.logger.info("Geeting TeacherListCreateAPIView information")
            teacherList = Teacher.objects.filter(user__username=kwargs["username"])
            self.logger.info(teacherList)
            serializers = TeacherProfileDetails(teacherList, many=True)
            self.logger.info(serializers.data)
            return Response(serializers.data)
        def patch(self, request, *args, **kwargs):
            teacher = Teacher.objects.get(user__username=kwargs['username'])
            serializers = TeacherProfileDetails(data=request.data, instance=teacher)
            self.logger.info(serializers)
            if serializers.is_valid():
                serializers.save()
                return Response(serializers.data, status=status.HTTP_201_CREATED)
            return Response(serializers.errors, status=status.HTTP_400_BAD_REQUEST)

Here is urls.py

path('teacher/<str:username>/details',
         TeacherListCreateAPIView.as_view(), name="teacher-details"),

Issue :

I am able to get the teacher details, but patch call not updating subject, looks like I am doing some mistake in TeacherProfileDetails while serializing subject

1 Answers1

2

SerializerMethodField is read-only. So, all fields in provided serializer are read-only.

You can use SlugRelatedField serializer instead, both for read and write:

subjects = serializers.SlugRelatedField(
    many=True,
    slug_field='subject',
    queryset=Subject.objects.all(),
)

For write better to add logic in the view to make subjects contain unique elements or even allow adding one subject from patch to already present subjects.


Or you can use two separate serializers - one for read (current one), and one for write (with just fields allowed to be changed - it can be SlugRelatedField, or even just ListField with custom update() method logic, etc).

Oleg Russkin
  • 4,234
  • 1
  • 8
  • 20
  • Getting below error, where should I write queryset ` super().__init__(**kwargs) File "/...../venv/lib/python3.7/site-packages/rest_framework/relations.py", line 108, in __init__ 'Relational field must provide a `queryset` argument, ' AssertionError: Relational field must provide a `queryset` argument, override `get_queryset`, or set read_only=`True`. ` – Rayees Namathponnan Jan 20 '20 at 02:13
  • Added queryset. – Oleg Russkin Jan 20 '20 at 05:53
  • Now it works, I am able to read and write with same SlugRelatedField, then why you recommend to have separate read and write – Rayees Namathponnan Jan 20 '20 at 06:30
  • For more readability / more explicit. Here almost all fields are read-only, hard to guess on first look that one field is for write. – Oleg Russkin Jan 20 '20 at 06:52
  • Also, SlugRelatedField may not be very intuitive in more advanced scenario - more interdependent fields, etc. – Oleg Russkin Jan 20 '20 at 06:53
  • Thanks, Final question can you please help below one also https://stackoverflow.com/questions/59818278/nested-dictionary-need-to-serialized-in-django – Rayees Namathponnan Jan 20 '20 at 08:19
  • i was creating a drf serializer where admins can add and removes users from a group. been having issues figuring it out until i came accross this anser. it solved my problem thanks. – Abayomi Olowu Apr 16 '23 at 11:25