1

I have a Land model with three relation which one of them is Letter and the model is:

class Letter(models.Model):
    land = models.ForeignKey('Land', on_delete=models.DO_NOTHING)
    image = models.ImageField(null=True, upload_to=letter_image_file_path)
    text = models.TextField(null=True, blank=True)

def __str__(self):
    return str(self.id)

and its serializer is

class LettersSerializer(serializers.ModelSerializer):

class Meta:
    model = Letter
    fields = ('id', 'text', 'image', 'land',)
    read_only_fields = ('id',)

and Land serializer is:

class LandSerializer(serializers.ModelSerializer):

    utm_points = UTMPointsSerializer(many=True, read_only=True)
    letters = LettersSerializer(many=True, read_only=True)

their views are :

class BasicViewSet(viewsets.ModelViewSet):
    authentication_classes = (TokenAuthentication,)
    permission_classes = (IsAuthenticated,)


class LandViewSet(BasicViewSet):
    serializer_class = LandSerializer
    queryset = Land.objects.all()

class UTMPointViewSet(BasicViewSet):
    serializer_class = UTMPointsSerializer
    queryset = UTMPoint.objects.all()


class LettersViewSet(BasicViewSet):
    serializer_class = LettersSerializer
    queryset = Letter.objects.all()

but when I send GET request it doesn't show letters field: here is the response:

{
    "id": 1,
    "utm_points": []
}

although utm_points and letters are exactly the same but they have different results. Land model has user relation which I removed it as simplicity. After some trials and errors I have no idea why the result does't have letters field.

Kourosh
  • 917
  • 1
  • 11
  • 24

1 Answers1

0

You need to explain to the LetterSerializer what serializer to use to serialize Land. You can not use the LandSerializer since that would create a circular dependency but more important: possibly infinite recursion in the response since a Letter has a land and the land then serializes its related letters, etc.

We thus make a simple serializer for Land:

class SimpleLandSerializer(serializers.ModelSerializer):
    utm_points = UTMPointsSerializer(many=True, read_only=True)

    class Meta:
        model = Land
        fields = ['utm_points']

and then use this in the serializer for land:

class LettersSerializer(serializers.ModelSerializer):
    land = SimpleLandSerializer()

    class Meta:
        model = Letter
        fields = ('id', 'text', 'image', 'land',)
        read_only_fields = ('id',)

EDIT: You can also serialize by using the primary key, in that case you can serialize with a PrimaryKeyRelatedField [drf-doc]:

class LettersSerializer(serializers.ModelSerializer):
    land = PrimaryKeyRelatedField()

    class Meta:
        model = Letter
        fields = ('id', 'text', 'image', 'land',)
        read_only_fields = ('id',)

EDIT 2: Another problem is that your letters relation does not exists, if you want to rename the relation from letter_set to letters, you use:

class Letter(models.Model):
    land = models.ForeignKey(
        'Land',
        related_name='letters',
        on_delete=models.DO_NOTHING
    )
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Actually I need to show letters in land response but you suggest `LettersSerializer` should show land serializer, on the other hand Letter and UTMPoints are exactly the same. so how come just letter wont be shown? – Kourosh Nov 28 '20 at 12:28
  • @moslem: because that would result in a serialization *cycle* where if you serialize a `Letter`, you serialize its `land`, that `land` will then serialize new `Letter`s which will again serialize land, so the JSON would look like `{'land': {'letters': [{'land': ...}]}}` and thus result in a response of infinite length. – Willem Van Onsem Nov 28 '20 at 12:30
  • No `LettersSerializer` just serializes `id` of `Land` you can see it in the definition of the serializer and `utm` has exactly the same implementation but without any problem – Kourosh Nov 28 '20 at 13:01
  • Your `LettersSerializer` has as `fieldds = ('id', 'text', 'image', 'land')`... – Willem Van Onsem Nov 28 '20 at 13:03
  • Yes but it just serializes id this is the output `{ "id": 2, "text": "test", "image": null, "land": 1 },` – Kourosh Nov 28 '20 at 13:27
  • @moslem: ah in that case you can use a `PrimaryKeyRelatedField`. – Willem Van Onsem Nov 28 '20 at 13:32
  • Thanks. but can you update the answer please – Kourosh Nov 28 '20 at 13:35
  • No, It doesn't work I need to serialize letters in land not land in letter it's already serialize land in letter without `PrimaryKeyRelatedField` – Kourosh Nov 28 '20 at 13:59
  • @moslem: but `letters` does not exists as a field. If you want to span a related field, you use `land = models.ForeignKey('Land', on_delete=models.DO_NOTHING, related_name='letters')`, otherwise it will use `letter_set`. – Willem Van Onsem Nov 28 '20 at 14:03