djangodjango-rest-frameworkgeneric-relations

How can I serialize (with Django REST API) when class relates to the same generic relation multiple times?


I have a class that has multiple references to the same generic class for different information:

class Resort(models.Model):
    id = models.PositiveIntegerField(_('HapNr.'), primary_key=True)
    symbol = models.CharField(_('Kurzzeichen'), max_length=3, blank=True)
    short_description = GenericRelation('MultiLingualText',
                                   verbose_name=_('Beschreibung (ML)'),
                                   related_query_name='resortshortdescriptions')
    long_description = GenericRelation('MultiLingualText',
                                   verbose_name=_('Einleitung (ML)'),
                                   related_query_name='resortlongdescription')

class MultiLingualText(models.Model):
    language = models.ForeignKey(hmodels.LanguageCode, verbose_name=_('Sprache'))
    valid_from = models.DateField(_('Gültig ab'), default=timezone.now)
    text = models.TextField(_('Text'))
    content_type = models.ForeignKey(ContentType, verbose_name=_('Typ'))
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey()
    atu_id = models.CharField(_('ATU Text Id'), max_length=12, editable=False, blank=True)
    atu_context = models.CharField(_('ATU Kontext'), max_length=1, editable=False, blank=True)

When I need to work with this class using the Django Admin I have two Inlines, each with a queryset selecting the correct texts for that relation. This works fine.

I tried doing something similar by using individual serializers and viewsets for each relation, but when I retrieve a resort, it still shows all texts with each relation.

class ResortSerializer(serializers.HyperlinkedModelSerializer):
    short_description = MultiLingualTextSerializerRSD(many=True, read_only=True)
    long_description = MultiLingualTextSerializerRLD(many=True, read_only=True)

    class Meta:
        model = Resort

class MultiLingualTextSerializerRSD(serializers.HyperlinkedModelSerializer):
    language = serializers.PrimaryKeyRelatedField(read_only=True)

    class Meta:
       model = MultiLingualText

class MultiLingualTextViewSetRSD(viewsets.ModelViewSet):
    serializer_class = MultiLingualTextSerializerRSD
    queryset = MultiLingualText.objects.exclude(atu_id='').order_by('resort', 'content_type', 'object_id',
                                                                '-valid_from')

class ResortViewSet(viewsets.ModelViewSet):
    queryset = Resort.objects.all().order_by('id')
    serializer_class = ResortSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_fields = ('id', 'sihot_nr')

So basically my question is, how can I use different querysets for each set of texts? Or is this at all possible?

Correct implementation (Thanks to @lucasnadalutti)

class ResortSerializer(serializers.HyperlinkedModelSerializer):
    short_description = serializers.SerializerMethodField()
    long_description = serializers.SerializerMethodField()

    def get_short_description(self, obj):
        qs = MultiLingualText.objects.exclude(atu_id='').order_by('-valid_from', 'language__code')
        return MultiLingualTextSerializer(qs, many=True, read_only=True).data

    def get_long_description(self, obj):
        qs = MultiLingualText.objects.filter(atu_id='').order_by('-valid_from', 'language__code')
        return MultiLingualTextSerializer(qs, many=True, read_only=True).data

Solution

  • MultiLingualTextViewSetRSD does not make much sense in this context, as what you want is to send the resorts and their descriptions in one request only, as it should be. In ordinary ForeignKey model field relations, I'm pretty sure that ResortSerializer would serialize only its related records as you expected, but I'm not sure about how DRF serializers work with generic relations.

    That said, one solution would be to replace:

    short_description = MultiLingualTextSerializerRSD(many=True, read_only=True)
    long_description = MultiLingualTextSerializerRLD(many=True, read_only=True)
    

    with:

    short_description = SerializerMethodField()
    long_description = SerializerMethodField()
    

    And implement your filtering-and-serialization inside get_short_description and get_long_description methods. Another solution is to remove both attributes and place this logic inside your serializer's to_representation method.