1
resposta

Nested Serializer com design patterns

Estou com certa dificuldade em criar esse serializador. Gostaria que os nested serializers fossem opcionais e aparecessem tanto pra criar, quanto pra visualizar (documentação). O grande problema é que em todos esses nested eu tenho ForeignKey() e não vejo nenhuma forma de fazer tudo isso automatizado pelo django, já que é preciso antes criar os itens que serão chaves estrangeiras. Gostaria de ter a possibilidade de criar essas foreign keys no próprio serializador mas em um método que não seja o create. Dessa forma cada serializador seria responsável por criar suas próprias Foreign Key e eu não precisaria sobrescrever o método create. Por exemplo, gostaria de criar um getter no serializador que seria o valor padrão de um dos campos a serem serializados (seja pra leitura ou pra escrita), assim eu não preciso ficar criando usuário dentro de um loop.

Sei que o SerializerMethodField() pode receber e mostrar um valor, mas somente pata leitura. Gostaria de algum campo customizavel no estilo do SerializerMethodField() mas que permitisse tanto a criação, quanto a leitura do objeto.

Além disso, gostaria que esse campo fosse AthleteGuardianSerializer(many=True)

Alguma dica?

class AthleteSerializer(ModelSerializer):

    guardian = AthleteGuardianSerializer(many=True,read_only=True)

    def create(self, validated_data):         

        services = AthleteInvitationService

        guardian_data = validated_data.pop('guardian', None)

        athlete_user = get_object_or_404(User, pk = validated_data.pop('user'))

        workspace = get_object_or_404(Workspace, pk = validated_data.pop('workspace'))

        athlete = Athlete.objects.create(user = athlete_user, workspace = workspace, **validated_data)

        athlete.save()

        if guardian_data:
            for parent in guardian_data:
                parent_user_factory = get_user_factory(parent)
                parent_user = services.get_or_create_basic_user(parent_user_factory)
                parent_instance = AthleteGuardianSerializer.objects.create(
                    user = parent_user, athlete = athlete, **parent
                )
                parent_instance.save()
1 resposta

Olá Raul, tudo bem com você?

Peço desculpas pela demora no retorno.

Quando se tem ForeignKey(), é preciso criar os itens que serão chaves estrangeiras antes de criar o objeto principal. Uma solução para criar as Foreign Keys no próprio serializador, sem sobrescrever o método create, é utilizar o método to_internal_value(). Esse método é responsável por converter os dados recebidos em um formato que o Django possa entender. Dessa forma, você pode criar suas Foreign Keys nesse método.

Quanto ao campo customizável que permita tanto a criação quanto a leitura do objeto, você pode utilizar o SerializerMethodField() em conjunto com o to_internal_value(). Assim, você pode criar um método que receba os dados e crie o objeto, e outro método que retorne o objeto criado.

Quanto à sua dúvida sobre o AthleteGuardianSerializer(many=True){ , você pode utilizar esse campo para criar vários objetos do tipo AthleteGuardianSerializer.

Segue um exemplo de como ficaria o seu código utilizando essas soluções:

class AthleteSerializer(ModelSerializer):

    guardian = AthleteGuardianSerializer(many=True, read_only=True)
    athletes_guardians = SerializerMethodField()

    def to_internal_value(self, data):
        guardian_data = data.pop('guardian', None)

        validated_data = super().to_internal_value(data)

        if guardian_data:
            validated_data['guardian'] = []
            for parent in guardian_data:
                parent_user_factory = get_user_factory(parent)
                parent_user = services.get_or_create_basic_user(parent_user_factory)
                parent_instance = AthleteGuardianSerializer(data={
                    'user': parent_user,
                    'athlete': validated_data['athlete'],
                    **parent
                })
                parent_instance.is_valid(raise_exception=True)
                validated_data['guardian'].append(parent_instance.save())

        return validated_data

    def create(self, validated_data):
        athlete_user = get_object_or_404(User, pk=validated_data.pop('user'))
        workspace = get_object_or_404(Workspace, pk=validated_data.pop('workspace'))
        athlete = Athlete.objects.create(user=athlete_user, workspace=workspace, **validated_data)
        athlete.save()
        return athlete

    def get_athletes_guardians(self, obj):
        guardians = AthleteGuardian.objects.filter(athlete=obj)
        return AthleteGuardianSerializer(guardians, many=True).data

    class Meta:
        model = Athlete
        fields = '__all__'

Todavia, vale ressaltar que como é um assunto externo ao curso e que não tenho acesso ao cenário completo do projeto incluindo a estrutura inicial do seu banco de dados, outros testes terão de ser feitos afim de obter o resultado esperado, mas espero que esta resposta seja um bom ponto de partida para a resolução do seu problema.

Espero ter ajudado. Continue mergulhando em conhecimento!

Abraços e bons estudos.

Caso este post tenha lhe ajudado, por favor, marcar como solucionado ✓. Bons Estudos!