Проблема с отображением отношения внешнего ключа в представлении

#django #django-rest-framework #django-serializer #django-rest-viewsets

#django #django-rest-framework #django-сериализатор #django-rest-viewsets

Вопрос:

У меня есть Land модель с тремя отношениями, одно из которых Letter и модель:

 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)
 

и его сериализатор

 class LettersSerializer(serializers.ModelSerializer):

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

и Land сериализатор:

 class LandSerializer(serializers.ModelSerializer):

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

их представления :

 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()
 

но когда я отправляю GET запрос, он не показывает letters поле: вот ответ:

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

хотя utm_points и letters точно такие же, но они имеют разные результаты.
Land модель имеет user отношение, которое я удалил для простоты.
После некоторых проб и ошибок я понятия не имею, почему в результате нет letters поля.

Ответ №1:

Вам нужно объяснить LetterSerializer , какой сериализатор использовать для сериализации Land . Вы не можете использовать LandSerializer , поскольку это создало бы циклическую зависимость, но что более важно: возможно, бесконечная рекурсия в ответе, поскольку a Letter имеет a land и land then serializes its related буквы и т.д.

Таким образом, мы создаем простой сериализатор для Land :

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

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

а затем используйте это в сериализаторе для land :

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

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

РЕДАКТИРОВАТЬ: вы также можете сериализовать с помощью первичного ключа, в этом случае вы можете сериализовать с помощью PrimaryKeyRelatedField [drf-doc]:

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

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

РЕДАКТИРОВАТЬ 2: Другая проблема заключается в том, что ваше letters отношение не существует, если вы хотите переименовать отношение из letter_set в letters , вы используете:

 class Letter(models.Model):
    land = models.ForeignKey(
        'Land',
        related_name='letters',
        on_delete=models.DO_NOTHING
    ) 

Комментарии:

1. На самом деле мне нужно показать буквы в ответе land, но вы предлагаете LettersSerializer показать сериализатор land, с другой стороны, Letter и UTMPoints точно такие же. так почему же просто буква не отображается?

2. @moslem: потому что это приведет к циклу сериализации, в котором, если вы сериализуете a Letter , вы сериализуете его land , что land затем сериализует новые Letter s, которые снова сериализуют land, так что JSON будет выглядеть {'land': {'letters': [{'land': ...}]}} и, таким образом, приведет к ответу бесконечной длины.

3. Нет LettersSerializer , просто сериализуется id Land , вы можете видеть это в определении сериализатора и utm имеет точно такую же реализацию, но без каких-либо проблем

4. У вас LettersSerializer есть as fieldds = ('id', 'text', 'image', 'land')

5. Да, но он просто сериализует идентификатор, который является результатом { "id": 2, "text": "test", "image": null, "land": 1 },