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 membersNow 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 memberThe 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?