1
resposta

Django update nested serializer

gente, eu já li a documentão, já corri atrás de muitos tutoriais mas eu to sempre parado no mesmo erro. Algo que devo estar faltando atenção, já não sei mais situação: eu tenho um model painel q recebe um nome e descrição

class Painel(models.Model):
    nome = models.CharField(max_length=100)
    descricao = models.CharField(max_length=100)

    def __str__(self):
        return self.nome

e tenho um model de links, em que, um painel tem um ou mais links, mas o link só pode ter um painel:

class Links(models.Model):
    link = models.CharField(max_length=500)
    duracao = models.IntegerField(default=30)
    ordem = models.IntegerField(default=1)
    painel = models.ForeignKey(Painel, default=1, on_delete=models.CASCADE, related_name='links_painel',
                               null=False, blank=False)

    def __str__(self):
        return f"{self.painel.nome}: {self.ordem}°"

eu tenho o serializer para fazer a criação que não me gera problema nenhum, depois de sofrer muito e entender que precisa passar o required como false, mas para fazer a atualização, não consigo eis o serializer:

class CriarPainelSerializer(serializers.ModelSerializer):
    links = LinkSerializer(many=True, required=False)

    class Meta:
        model = Painel
        fields = ['id', 'nome', 'descricao', 'links']

    def create(self, validated_data):
        links_data = validated_data.pop('links')
        painel = Painel.objects.create(**validated_data)

        for link_data in links_data:
            Links.objects.create(painel=painel, **link_data)

            # Obtém o painel recém-criado com os links associados
        painel_com_links = Painel.objects.prefetch_related('links_painel').get(id=painel.id)

        # Serializa o painel com os links
        serializer = RetornarPaineisSerializer(painel_com_links)
        return serializer.data

    def update(self, instance, validated_data):
        links_data = validated_data.pop('links')
        instance.nome = validated_data.get("nome", instance.nome)
        instance.descricao = validated_data.get("descricao", instance.descricao)
        instance.save()
        manter_links = []
        ids_existentes = [link.id for link in instance.links]
        for link_data in links_data:
            if "id" in links_data.keys():
                if Links.objects.filter(id=link_data["id"]).exists():
                    c = Links.objects.get(id=link_data["id"])
                    c.link = link_data.get('link', c.link)
                    c.duracao = link_data.get('duracao', c.duracao)
                    c.ordem = link_data.get('ordem', c.ordem)
                    c.save()
                    manter_links.append(c.id)
                else:
                    continue
            else:
                c = Links.objects.create(**link_data, painel=instance)
                manter_links.append(c.id)

        for link in instance.links:
            if link.id not in manter_links:
                link.delete()

        return instance

o erro q o terminal me gera é: 'Painel' object has no attribute 'links'

quando no inicio estou chamando q links = LinkSerializer(many=True, required=False) e chamo no campo fields ele cria, mas nao consegue atualizar, ele consegue separar no links_data = validated_data.pop('links') mas no update nao entende..

alguem consegue ajudar a desenrolar esse mistério ai?

a minha última tentativa de update, tentei com as dicas desse cara, que deram super certas pra ele.. mas pra mim.. https://www.youtube.com/watch?v=EyMFf9O6E60

1 resposta

E aí, Ariel!

Parece que tá rolando uma confusão entre o related_name e o nome do campo. Pelo que eu vi, o teu related_name é 'links_painel', então é isso que tu precisa usar no teu serializer, ajusta isso no teu serializer de atualização (update)!!

Em vez de for link in instance.links:, usa for link in instance.links_painel.all(): pq o related_name que tu definiu é 'links_painel'.

for link in instance.links_painel.all():
    if link.id not in manter_links:
        link.delete()

E na hora de percorrer os links na parte do update, muda de for link_data in links_data: para for link_data in links_data.copy(): pq, ao que parece, tu tá mexendo no dicionário enquanto itera sobre ele, o que pode gerar uns comportamentos estranhos.