Уплощение вложенного сериализатора DRF

У меня есть две модели для каждого пользователя, модель User и модель Profile.

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)

У пользователя может быть несколько профилей, но для каждого locale у пользователя может быть только один профиль. И я использую эти модели как основу для создания других моделей пользователей, например, модель Member:

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

Теперь для списка и создания функциональности этой Member модели я написал ListCreateAPIView следующим образом:

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

Который использует MemberSerializer следующим образом:

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

Схожи UserSerializer и ProfileSerializer:

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",
        )

Это работает, и схема входных и ответных данных соответствует ожиданиям:

Ответ:

{
  "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"
    }
  ]
}

вход:

{
  "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"
}

Я могу получить locale от клиента через url, и я хочу сгладить profiles объекты во вводе и ответе. Для ответа я могу использовать to_representation и to_internal_value для создания только одного профиля и возврата только одного профиля на основе переменной locale, переданной в url, но я не знаю, как я могу полностью сгладить объект profiles, чтобы получить эти схемы для ввода и ответа:

Ответ:

{
  "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"
    }
  ]
}

вход:

{
  "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"
}

Как я могу это сделать?

Вернуться на верх