Django Rest Framework, гиперссылка на вложенные отношения

У меня есть две модели: Пользователь и Билет. Заявка принадлежит одному Пользователю, Пользователь имеет много Заявок.

Я добился этого, когда я перехожу по URL-адресу /users/1/tickets, я получаю список билетов пользователя.

Я хочу использовать отношения с гиперссылками, и вот что я вижу в представлении модели пользователя:

"tickets": [
    "http://127.0.0.1:8000/tickets/5/", 
    "http://127.0.0.1:8000/tickets/6/"
]

Но я хочу, чтобы это было похоже

"tickets": "http://127.0.0.1:8000/users/1/tickets"

Есть ли способ сделать это с помощью DRF?

URL-адрес:

    url(r'^users/(?P<user_pk>\d+)/tickets/$',
    views.TicketsByUserList.as_view(),
    name='myuser-tickets'),

Вид:

class TicketsByUserList(generics.ListAPIView):
    model = Ticket
    serializer_class = TicketSerializer

    def get_queryset(self):
        user_pk = self.kwargs.get('user_pk', None)
        if user_pk is not None:
            return Ticket.objects.filter(user=user_pk)
        return []

Пользовательский сериализатор (я пытался поиграть с определением поля тикетов, изменением типа, view_name, но безрезультатно):

class UserSerializer(serializers.HyperlinkedModelSerializer):
    tickets = serializers.HyperlinkedRelatedField(many=True, view_name='ticket-detail')

    class Meta:
        model = MyUser
        fields = ('id', 'nickname', 'email', 'tickets')

Сериализатор билетов:

class TicketSerializer(serializers.HyperlinkedModelSerializer):
    user = serializers.HyperlinkedRelatedField(view_name='myuser-detail')
    liked = serializers.Field(source='liked')

    class Meta:
        model = Ticket
        fields = ('id', 'user', 'word', 'transcription', 'translation', 'liked', 'created', 'updated')

person optimistiks    schedule 04.11.2013    source источник


Ответы (2)


Вы можете использовать SerializerMethodField, чтобы настроить его. Что-то вроде этого:

class UserSerializer(serializers.HyperlinkedModelSerializer):
    tickets = serializers.SerializerMethodField('get_tickets')

    def get_tickets(self, obj):
        return "http://127.0.0.1:8000/users/%d/tickets" % obj.id

    class Meta:
        model = MyUser
        fields = ('id', 'nickname', 'email', 'tickets')

Для краткости я вставил туда URL-адрес, но вы также можете выполнить обратный поиск. По сути, это просто говорит ему вызывать метод get_tickets вместо поведения по умолчанию в суперклассе.

person Joe Holloway    schedule 04.11.2013
comment
Пытаясь реализовать ваше решение, я понял, что мне нужно каким-то образом передать объект запроса сериализатору, чтобы передать его обратной функции, чтобы получить абсолютный URL-адрес. Я решил это, передав объект запроса из метода отправки представления в поле в моем сериализаторе. Но я чувствую, что переопределять метод отправки каждый раз, когда я хочу иметь объект запроса в своем сериализаторе, не очень хорошая идея. Как лучше всего получить запрос в сериализаторе? - person optimistiks; 07.11.2013
comment
В методе get_tickets вы можете получить объект запроса с помощью request = self.context.get('request'), а затем вызвать reverse как обычно. - person Cartucho; 23.06.2015
comment
@JoeHolloway Что мне делать, если мне нужно вернуть объекты набора запросов, потому что это дает мне ошибку объекта, не сериализуемого JSON. - person CrazyGeek; 23.02.2016
comment
@CrazyGeek не уверен, понимаю ли я ваш вопрос, но в более новых версиях DRF вам нужно передать many=True при создании сериализатора, если вы сериализуете более одного объекта. - person Joe Holloway; 23.02.2016

Для справки, вот пример полного решения, основанного на ответе Джо Холлоуэя:

from rest_framework.reverse import reverse

class WorkProjectSerializer(serializers.CustomSerializer):
   issues = drf_serializers.SerializerMethodField()

    def get_issues(self, obj):
        request = self.context.get('request')
        return request.build_absolute_uri(reverse('project-issue-list', kwargs={'project_id': obj.id}))

    class Meta:
        model = WorkProject
        fields = '__all__'
person Mario    schedule 23.08.2017