Структура Django REST: может ли вложенный объект получить доступ к данным родительского объекта в представлении списка?
Я пытаюсь реализовать вложенный ресурс, где одно из его полей зависит от значения от его родительского ресурса.
Предположим, что мы строим систему для компании, которая предоставляет информацию о своих клиентах и данные о продажах для продавцов компании. Итак, у нас есть две модели: Клиент и Rep. Реп может продавать более чем одному клиенту.
URL, который возвращает всех клиентов: /api/1.0/customers/
URL для конкретного клиента: /api/1.0/customers/123/
URL-адрес для конкретной информации о конкретном продавце: /api/1.0/customers/123/rep/9/
Обратите внимание, что URL-адрес rep содержит идентификатор клиента, а также идентификатор rep.
Я хочу, чтобы URL-адрес клиента возвращал вложенный ресурс, содержащий сводную информацию о репутации, плюс гиперссылку на полную информацию о клиенте для этой репутации. Это результат URL для всех клиентов:
[
{
"id": 100,
"customer_name": "DolManSaxLil",
"rep": {
"id": 4,
"annual_sales": 1500.01,
"name": "Fred",
"url": "http://localhost:8000/api/1.0/customer/100/rep/4/"
}
},
{
"id": 200,
"customer_name": "Hotblack",
"rep": {
"id": 4,
"annual_sales": 10500.42,
"name": "Fred",
"url": "http://localhost:8000/api/1.0/customer/200/rep/4/"
}
}
]
Для реализации этого мы определяем два сериализатора:
class CustomerSummarySerializer(serializers.HyperlinkedModelSerializer):
id = ...
name = ...
rep = RepSummarySerializer(read_only=True)
class RepSummarySerializer(serializers.HyperlinkedModelSerializer):
id = ...
annual_sales = ...
name = ....
url = serializers.SerializerMethodField('get_rep_url')
Проблема, с которой я сталкиваюсь, заключается в том, что я не могу решить, как получить доступ к текущему customer.id из функции RepSummarySerializer.get_rep_url
. Это возможно в подробном представлении, поскольку клиент находится в self.parent.obj
:
def get_rep_url(self, obj):
customer_id = self.parent.obj.id
url = reverse('api_customer_rep',
kwargs={'customer_id': customer_id,
'rep_id': obj.id},
request=serializer.context.get('request'))
return url
Однако в представлении списка self.parent.obj
является объектом QuerySet объектов Customer, и я не вижу способа идентифицировать текущего клиента. Есть ли способ сделать это? Я пропустил что-то очевидное?
Ответы
Ответ 1
Момент ясности: решение состоит в использовании SerializerMethodField
для создания экземпляра RepSummarySerializer
и передачи customer_id
в контексте:
class CustomerSummarySerializer(serializers.HyperlinkedModelSerializer):
id = ...
name = ...
rep = serializers.SerializerMethodField('get_rep')
def get_rep(self, obj):
rep = obj.rep
serializer_context = {'request': self.context.get('request'),
'customer_id': obj.id}
serializer = RepSummarySerializer(rep, context=serializer_context)
return serializer.data
Теперь в customer_id
можно получить доступ к RepSummarySerializer.get_rep_url
следующим образом:
def get_rep_url(self, obj):
customer_id = self.context.get('customer_id')
...
Не знаю, почему я не думал об этом три часа назад.
Ответ 2
В дополнение к принятому ответу, если вы используете представления и хотите, чтобы ваш подресурс был только коллекцией (только отфильтрованной родительским документом), вы также можете использовать декоратор @detail_route
, как задокументировано в документах:
from rest_framework import viewsets
from rest_framework.decorators import detail_route
from rest_framework.response import Response
class CustomerViewSet(viewsets.ModelViewSet):
queryset = Customer.objects.all()
serializer_class = CustomerSummarySerializer
...
@detail_route(methods=['get'])
def rep(self, request, pk=None):
customer = self.get_object()
queryset = customer.pk.all()
instances = self.filter_queryset(queryset)
serializer = RepSummarySerializer(instances,
context={'request': request}, many=True)
return Response(serializer.data)
Теперь вы можете запросить /customers/123/rep/
, и вы получите список всех экземпляров Rep
для указанного клиента.
Вероятно, он не будет полностью решать то, что вам нужно, но для многих людей, которым не нужны полные вложенные ресурсы, на самом деле это достаточно.