Уплощение вложенного сериализатора 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"
}
Как я могу это сделать?