2013-08-30 13 views
15

Tôi đang cố gắng triển khai tài nguyên lồng nhau trong đó một trong các trường của nó phụ thuộc vào giá trị từ tài nguyên gốc của nó.Khung công tác Django REST: Một đối tượng lồng nhau có thể truy cập thông tin chi tiết của đối tượng cha của nó trong Chế độ xem danh sách không?

Giả sử chúng tôi đang xây dựng hệ thống cho một công ty cung cấp thông tin về khách hàng cùng với số liệu bán hàng cho nhân viên bán hàng của công ty. Vì vậy, chúng tôi có hai mô hình, Khách hàngĐại diện. Đại diện có thể bán cho nhiều khách hàng.

URL trả về tất cả khách hàng: /api/1.0/customers/

URL cho một khách hàng cụ thể: /api/1.0/customers/123/

URL cho thông tin khách hàng cụ thể đối với một đại diện bán hàng cụ thể: /api/1.0/customers/123/rep/9/

Note URL đại diện chứa các khách hàng ID cũng như ID đại diện.

Tôi muốn URL của khách hàng trả lại tài nguyên lồng nhau chứa thông tin tóm tắt về đại diện, cộng với siêu liên kết đến thông tin đầy đủ dành riêng cho khách hàng cho đại diện đó. Đây là sản phẩm từ các URL cho tất cả khách hàng:

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

Để thực hiện điều này chúng ta định nghĩa hai 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') 

Vấn đề tôi đang phải đối mặt là tôi không thể làm việc ra làm thế nào để truy cập vào khách hàng hiện tại .id từ hàm RepSummarySerializer.get_rep_url. Có thể trong một cái nhìn chi tiết như khách hàng được tổ chức tại 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 

Tuy nhiên, trong một cái nhìn danh sách, self.parent.obj là một QuerySet của các đối tượng khách hàng và tôi không thể nhìn thấy bất cứ cách nào xác định các khách hàng hiện tại. Có cách nào để làm điều này không? Tôi đã bỏ lỡ một cái gì đó hiển nhiên?

Trả lời

20

Moment rõ ràng: giải pháp là sử dụng một SerializerMethodField để nhanh chóng RepSummarySerializer và vượt qua customer_id trong bối cảnh:

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 

Các customer_id bây giờ có thể được truy cập trong RepSummarySerializer.get_rep_url như thế này:

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

Không biết tại sao tôi không nghĩ về điều này ba giờ trước.

+0

Khá một giải pháp thanh lịch.Và hoạt động hoàn hảo! Cảm ơn rất nhiều! – gregoltsov

+0

Giải pháp tuyệt vời, nhưng tôi phải thay đổi rep = obj.rep thành rep = obj.rep.get() –

2

Ngoài các câu trả lời được chấp nhận, nếu bạn sử dụng viewsets và muốn tiểu tài nguyên của bạn trở thành một bộ sưu tập (lọc bởi tài liệu gốc) duy nhất, bạn cũng có thể sử dụng @detail_route trang trí, như 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) 

Bây giờ bạn có thể truy vấn /customers/123/rep/ và bạn sẽ nhận được danh sách tất cả các trường hợp Rep cho khách hàng được chỉ định.

Nó có thể sẽ không giải quyết đầy đủ những gì bạn cần, nhưng đối với nhiều người không cần tài nguyên lồng nhau đầy đủ nó thực sự là đủ.

Các vấn đề liên quan