Quantcast
Channel: Active questions tagged rest - Stack Overflow
Viewing all articles
Browse latest Browse all 4797

Flatten DRF's nested serializer

$
0
0

I have two models for each user, a User model and a Profile model.

class User(BaseModel, AbstractUser):    username = None    image = models.CharField(max_length=300, blank=True, null=True)    email = models.EmailField(unique=True)    password = models.CharField(max_length=300)    emailConfirmed = models.BooleanField(default=False)    phoneNumber = models.CharField(max_length=15, blank=True, null=True)    phoneNumberConfirmed = models.BooleanField(default=False)    USERNAME_FIELD = "email"    REQUIRED_FIELDS = []    objects = UserManager()class Profile(BaseModel):    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='profiles', null=True, blank=True)    firstName = models.CharField(max_length=30)    lastName = models.CharField(max_length=30)    locale = models.CharField(max_length=2)

A user can have multiple profiles, but for each locale, a user can have only one profile. And I use these models as a base to create other user models, for example, a Member model:

class Member(User):    birthdate = models.DateField(blank=True, null=True)class MemberProfile(Profile):    member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name='member_profiles', null=True, blank=True)    education = models.CharField(max_length=300, blank=True, null=True)    address = models.CharField(max_length=300, blank=True, null=True)    # other fields only for members

Now for the list and create functionalities of this Member model, I've written a ListCreateAPIView like this:

@extend_schema(tags=[Namespace.MEMBERS.value])class MemberListCreateView(generics.ListCreateAPIView):    serializer_class = MemberSerializer    permission_classes = [IsAuthenticated, IsAdminUser]    filter_backends = [filters.DjangoFilterBackend]    filterset_class = SearchFilter    http_method_names = ["get", "post"]    def get_queryset(self):        return Member.objects.all()    def get_serializer_context(self):        context = super().get_serializer_context()        context["locale"] = self.kwargs.get("locale", None)        return context    def create(self, request, *args, **kwargs):        return super().create(request, *args, **kwargs)    def list(self, request, *args, **kwargs):        queryset = self.filter_queryset(self.get_queryset())        page = self.paginate_queryset(queryset)        if page is not None:            serializer = self.get_serializer(page, many=True)            return self.get_paginated_response(serializer.data)        serializer = self.get_serializer(queryset, many=True)        response = {"items": serializer.data,"total": queryset.count(),        }        return Response(data=response, status=status.HTTP_200_OK)

Which uses a MemberSerializer like this:

class MemberProfileSerializer(ProfileSerializer):    class Meta:        model = MemberProfile        fields = ProfileSerializer.Meta.fields + ("education","address",            # other fields only for members        )class MemberSerializer(UserSerializer):    password = PasswordField()    profiles = MemberProfileSerializer(many=True, source="member_profiles")    class Meta:        model = Member        fields = UserSerializer.Meta.fields + ("birthdate",        )    def create(self, validated_data):        profiles = validated_data.pop("member_profiles")        member = Member.objects.create(**validated_data)        for profile in profiles:            MemberProfile.objects.create(member=member, **profile)        return member

The UserSerializer and ProfileSerializer are similar:

class ProfileSerializer(serializers.ModelSerializer):    class Meta:        model = Profile        fields = ("id","firstName","lastName","locale",        )class UserSerializer(serializers.ModelSerializer):    password = PasswordField()    profiles = ProfileSerializer(many=True)    class Meta:        model = User        fields = ("id","image","email","password","emailConfirmed","phoneNumber","phoneNumberConfirmed","profiles",        )

This works and the schema for the input and response is as expected:

response:

{"total": 123,"items": [    {"id": 0,"image": "string","email": "user@example.com","emailConfirmed": true,"phoneNumber": "string","phoneNumberConfirmed": true,"profiles": [        {"id": 0,"firstName": "string","lastName": "string","locale": "st","education": "string","address": "string",          # other fields only for members        }      ],"birthdate": "2024-05-19"    }  ]}

input:

{"image": "string","email": "user@example.com","password": "string","emailConfirmed": true,"phoneNumber": "string","phoneNumberConfirmed": true,"profiles": [    {"firstName": "string","lastName": "string","locale": "st","education": "string","address": "string",      # other fields only for members    }  ],"birthdate": "2024-05-19"}

I can get the locale from client through the url, and I wish to flatten the profiles objects in the input and response. For the response, I can use to_representation and to_internal_value to create only one profile and return only one profile based on the locale variable passed in the url, but I don't know how I can completely flatten the profiles object to get these schemas for the input and response:

response:

{"total": 123,"items": [    {"id": 0,"image": "string","email": "user@example.com","emailConfirmed": true,"phoneNumber": "string","phoneNumberConfirmed": true,"firstName": "string","lastName": "string","locale": "st","education": "string","address": "string",      # other fields only for members"birthdate": "2024-05-19"    }  ]}

input:

{"image": "string","email": "user@example.com","password": "string","emailConfirmed": true,"phoneNumber": "string","phoneNumberConfirmed": true,"firstName": "string","lastName": "string","locale": "st","education": "string","address": "string",  # other fields only for members"birthdate": "2024-05-19"}

How can I do that?


Viewing all articles
Browse latest Browse all 4797

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>