2013-08-30 13 views
15

Próbuję zaimplementować zagnieżdżony zasób, w którym jedno z jego pól zależy od wartości z jego zasobu nadrzędnego.Django REST framework: czy zagnieżdżony obiekt może uzyskać dostęp do szczegółów obiektu nadrzędnego w widoku listy?

Załóżmy, że budujemy system dla firmy, która dostarcza informacji o swoich klientach oraz danych o sprzedaży dla sprzedawców firmy. Mamy więc dwa modele: Klient i Rep. Rep może sprzedać więcej niż jednego klienta.

URL, które zwraca wszystkich klientów: /api/1.0/customers/

URL dla konkretnego klienta: /api/1.0/customers/123/

URL informacji o klientach specyficzne dla konkretnego przedstawiciela handlowego: /api/1.0/customers/123/rep/9/

Uwaga URL rep zawiera klienta ID, a także identyfikator rep.

Chcę, aby adres URL klienta zwrócił zagnieżdżony zasób zawierający informacje podsumowujące o przedstawicielu oraz hiperłącze do pełnych informacji specyficznych dla klienta dla tego przedstawiciela. Jest to wyjście z adresu URL dla wszystkich klientów:

[ 
    { 
     "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/" 
       } 
    } 
] 

Aby to realizować możemy zdefiniować dwa serializers:

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

Problem jestem stoi to, że nie może pracować się, jak uzyskać dostęp do aktualnego klienta .id z funkcji RepSummarySerializer.get_rep_url. Jest to możliwe w widoku szczegółów, jak klient jest utrzymywany w 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 

Jednak w widoku listy, self.parent.obj jest QuerySet obiektów klienta i nie widzę żadnego sposobu identyfikacji bieżącego klienta. Czy jest jakiś sposób na zrobienie tego? Czy przegapiłem coś oczywistego?

Odpowiedz

20

Moment klarowność: rozwiązaniem jest użycie SerializerMethodField do instancji RepSummarySerializer i przekazać customer_id w kontekście:

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 znajduje się teraz w RepSummarySerializer.get_rep_url tak:

def get_rep_url(self, obj): 
    customer_id = self.context.get('customer_id') 
    ... 

Nie wiem, dlaczego nie myślałem o tym trzy godziny temu.

+0

Całkiem eleganckie rozwiązanie.Działa bez zarzutu! Wielkie dzięki! – gregoltsov

+0

Świetne rozwiązanie, ale musiałem zmienić rep = obj.rep na rep = obj.rep.get() –

2

Oprócz przyjętej odpowiedź, jeśli używasz viewsets i chcesz sub-resource się zbiór (filtrowane przez dokumentu macierzystego) tylko, można również użyć @detail_route dekorator, jak documented in the docs:

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) 

Teraz możesz zapytać /customers/123/rep/, a otrzymasz listę wszystkich instancji Rep dla określonego klienta.

Prawdopodobnie nie rozwiąże to w pełni tego, czego potrzebujesz, ale dla wielu osób, które nie potrzebują pełnych zasobów zagnieżdżonych, wystarczy.

Powiązane problemy