I want to update the Model data using nested serializers via PATCH call in DRF.

I have custom User Model, Country, City, Detail Model. I have MainUserSerializer, CountrySerializer, CitySerializer and UserSerializer, and I have UserAPI view.

Below is my models.py:

class User(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(max_length=254, null=True, blank=True)
    email = models.EmailField(max_length=254, unique=True)
    first_name = models.CharField(max_length=254, null=True, blank=True)
    last_name = models.CharField(max_length=254, null=True, blank=True)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    last_login = models.DateTimeField(null=True, blank=True)
    date_joined = models.DateTimeField(auto_now_add=True)
    
    USERNAME_FIELD = 'email'
    EMAIL_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = UserManager()

    def get_absolute_url(self):
        return "/users/%i/" % (self.pk)


class Country(models.Model):
    name = models.CharField(max_length=100, default='c')

    def __str__(self):
        return ("{}".format(self.name))


class City(models.Model):
    name = models.CharField(max_length=100 ,default='c')
    country = models.ForeignKey(Country, on_delete=models.CASCADE)

    def __str__(self):
        return ("{} ({})".format(self.name, self.country.name))


class Detail(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    gender = models.CharField(max_length=20, default='g')
    age = models.IntegerField(default=0)
    country = models.IntegerField(default=1)
    city = models.IntegerField(default=1)

    def __str__(self):
        return ("{} ({} {})".format(self.user.email, self.user.first_name, self.user.last_name))

my serializers.py:

class MainUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username', 'first_name', 'last_name', 'email')

    
class CitySerializer(serializers.ModelSerializer):
    class Meta:
        model = City
        fields = ('name',)


class CountrySerializer(serializers.ModelSerializer):
    city = CitySerializer()

    class Meta:
        model = Country
        fields = ('name', 'city')


class UserSerializer(serializers.ModelSerializer):
    usr = MainUserSerializer()
    country = CountrySerializer()
    city = CitySerializer()

    class Meta:
        model = Detail
        fields = ('usr', 'gender', 'age', 'country', 'city')

    def update(self, instance, validated_data):
        instance = super().update(instance, validated_data)
        
        instance.save()
            
        return instance

and my views.py is:

class UserAPI(generics.GenericAPIView):
    permission_classes = [AllowAny]
    serializer_class = UserSerializer

    def get(self, request, id):
        detail = Detail.objects.get(user=User.objects.get(pk=id))
        usr = User.objects.get(pk=id)
        
        data = {
            'id': usr.id,
            'username': usr.username,
            'first_name': usr.first_name,
            'last_name': usr.last_name,
            'email': usr.email,
            'gender': detail.gender,
            'age': detail.age,
            'country': detail.country,
            'city': detail.city

        }

        return Response(data)

    def patch(self, request, id):
        usr = User.objects.get(pk=id)     
        detail = Detail.objects.get(user=usr)
        serializer = self.get_serializer(detail, data=request.data, partial=True)
        print(request.POST)
        
        if serializer.is_valid():
            serializer.save()
            print("SUCCESS")
            return Response(code=200)

        return JsonResponse(data="wrong parameters", safe=False)

Error Details:

Error Name: AttributeError: 'NoneType' object has no attribute '_meta'

Full Error:

Traceback Switch to copy-and-paste view
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\django\core\handlers\exception.py, line 55, in inner
                response = get_response(request) …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\django\core\handlers\base.py, line 220, in _get_response
                response = response.render() …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\django\template\response.py, line 114, in render
            self.content = self.rendered_content …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\rest_framework\response.py, line 70, in rendered_content
        ret = renderer.render(self.data, accepted_media_type, context) …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\rest_framework\renderers.py, line 724, in render
        context = self.get_context(data, accepted_media_type, renderer_context) …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\rest_framework\renderers.py, line 657, in get_context
        raw_data_patch_form = self.get_raw_data_form(data, view, 'PATCH', request) …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\rest_framework\renderers.py, line 563, in get_raw_data_form
                data = serializer.data.copy() …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\rest_framework\serializers.py, line 555, in data
        ret = super().data …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\rest_framework\serializers.py, line 257, in data
                self._data = self.get_initial() …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\rest_framework\serializers.py, line 403, in get_initial
        return OrderedDict([ …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\rest_framework\serializers.py, line 404, in <listcomp>
            (field.field_name, field.get_initial()) …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\rest_framework\serializers.py, line 405, in get_initial
            for field in self.fields.values() …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\django\utils\functional.py, line 49, in __get__
        res = instance.__dict__[self.name] = self.func(instance) …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\rest_framework\serializers.py, line 356, in fields
        for key, value in self.get_fields().items(): …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\rest_framework\serializers.py, line 1051, in get_fields
        info = model_meta.get_field_info(model) …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\rest_framework\utils\model_meta.py, line 39, in get_field_info
    forward_relations = _get_forward_relationships(opts) …
Local vars
C:\Users\Khubaib Khawar\AppData\Local\Programs\Python\Python310\lib\site-packages\rest_framework\utils\model_meta.py, line 96, in _get_forward_relationships
                not field.remote_field.through._meta.auto_created …
Local vars

Anyone please guide me why I am getting this error.

1

There are 1 best solutions below

4
On

You wrote usr instead of user in your serializer:

class UserSerializer(serializers.ModelSerializer):
    user = MainUserSerializer()  # Here
    country = CountrySerializer()
    city = CitySerializer()

    class Meta:
        model = Detail
        fields = ('user', 'gender', 'age', 'country', 'city')  # Here

    def update(self, instance, validated_data):
        instance = super().update(instance, validated_data)
        
        instance.save()
            
        return instance