2013-08-04 43 views
6

Tôi có API REST khung thời gian còn lại của django với tài nguyên phân cấp. Tôi muốn để có thể tạo subobjects bằng cách POSTing để /v1/objects/<pk>/subobjects/ và có nó tự động thiết lập các khóa nước ngoài trên subobject mới để pk kwarg từ URL mà không cần phải đặt nó trong tải trọng. Hiện tại, serializer đang gây ra lỗi 400, bởi vì nó mong đợi khóa ngoài object nằm trong tải trọng, nhưng nó cũng không được coi là tùy chọn. URL của các subobject là /v1/subobjects/<pk>/ (vì khóa của phụ huynh không cần thiết để xác định nó), vì vậy nó vẫn còn cần thiết nếu tôi muốn PUT một tài nguyên hiện có.Django REST Framework: tạo các đối tượng phân cấp bằng cách sử dụng đối số URL

Tôi có nên tạo nó để bạn đăng lên /v1/subobjects/ với phụ huynh trong tải trọng để thêm các subobject, hoặc có cách nào tốt để chuyển số pk kwarg từ URL tới bộ nối tiếp không? Tôi đang sử dụng HyperlinkedModelSerializerModelViewSet làm các lớp cơ sở tương ứng của mình. Có cách nào được khuyến nghị thực hiện việc này không? Cho đến nay ý tưởng duy nhất tôi có là hoàn toàn thực hiện lại ViewSets và tạo một lớp Serializer tùy chỉnh có get_default_fields() xuất phát từ một từ điển được truyền vào từ ViewSet, được điền bởi các kwargs của nó. Điều này có vẻ khá liên quan đến một cái gì đó mà tôi đã có thể nghĩ là hoàn toàn chạy-of-the-mill, vì vậy tôi không thể không nghĩ rằng tôi đang thiếu một cái gì đó. Mỗi API REST mà tôi từng thấy có các điểm cuối có thể ghi được đều có kiểu suy luận đối số dựa trên URL này, vì vậy thực tế là khung công tác django-rest-dường như không thể làm điều đó có vẻ lạ.

Trả lời

3

Làm cho trường bộ nối tiếp đối tượng chính là read_only. Nó không phải là tùy chọn nhưng nó cũng không đến từ dữ liệu yêu cầu. Thay vào đó bạn kéo pk/sên từ URL trong pre_save() ...

# Assuming list and detail URLs like: 
# /v1/objects/<parent_pk>/subobjects/ 
# /v1/objects/<parent_pk>/subobjects/<pk>/ 
def pre_save(self, obj): 
    parent = models.MainObject.objects.get(pk=self.kwargs['parent_pk']) 
    obj.parent = parent 
+0

'pre_save' không được gọi cho đến sau khi deserialization, tại thời điểm đó nó đã ném một lỗi xác nhận. –

+0

Ah, được rồi. Đã cập nhật câu trả lời. –

+0

Đó là một lựa chọn và sẽ làm việc cho một cái gì đó như bình luận, nhưng một số các đối tượng tôi có thể được reparented, do đó, thiết lập các phụ huynh một cách rõ ràng trong một PUT sẽ được tốt đẹp quá. Mặt khác, tôi có thể làm theo cách này và sau đó có một hành động POST 'reparent' rõ ràng hoặc một cái gì đó, nhưng điều đó dường như một chút ít RESTful. Tôi sẽ chấp nhận câu trả lời này vì đó là một ý tưởng hay và sẽ làm việc cho đa số trường hợp. –

1

Đây là những gì tôi đã làm để giải quyết nó, mặc dù nó sẽ là tốt đẹp nếu có một cách tổng quát hơn để làm điều đó, vì nó là một mẫu URL phổ biến. Trước tiên tôi tạo ra một mixin cho ViewSets tôi rằng định nghĩa lại các create phương pháp:

class CreatePartialModelMixin(object): 
    def initial_instance(self, request): 
     return None 

    def create(self, request, *args, **kwargs): 
     instance = self.initial_instance(request) 
     serializer = self.get_serializer(
      instance=instance, data=request.DATA, files=request.FILES, 
      partial=True) 

     if serializer.is_valid(): 
      self.pre_save(serializer.object) 
      self.object = serializer.save(force_insert=True) 
      self.post_save(self.object, created=True) 
      headers = self.get_success_headers(serializer.data) 
      return Response(
       serializer.data, status=status.HTTP_201_CREATED, 
       headers=headers) 

     return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 

Chủ yếu nó được sao chép và dán từ CreateModelMixin, nhưng nó định nghĩa một phương pháp initial_instance rằng chúng ta có thể ghi đè trong các lớp con để cung cấp một điểm khởi đầu cho các serializer , được thiết lập để thực hiện một phần deserialization. Sau đó, tôi có thể làm, ví dụ,

class SubObjectViewSet(CreatePartialModelMixin, viewsets.ModelViewSet): 
    # .... 

    def initial_instance(self, request): 
     instance = models.SubObject(owner=request.user) 
     if 'pk' in self.kwargs: 
      parent = models.MainObject.objects.get(pk=self.kwargs['pk']) 
      instance.parent = parent 
     return instance 

(tôi nhận ra tôi không thực sự cần phải làm một .get trên pk để kết hợp nó vào mô hình, nhưng trong trường hợp của tôi, tôi đang phơi bày sên hơn khóa chính trong API công khai)

0

Nếu bạn đang sử dụng ModelSerializer (được thực hiện bởi HyperlinkedModelSerializer) nó dễ dàng như việc thực hiện các phương pháp restore_object():

class MySerializer(serializers.ModelSerializer): 

    def restore_object(self, attrs, instance=None): 

     if instance is None: 
      # If `instance` is `None`, it means we're creating 
      # a new object, so we set the `parent_id` field. 
      attrs['parent_id'] = self.context['view'].kwargs['parent_pk'] 

     return super(MySerializer, self).restore_object(attrs, instance) 

    # ... 

restore_object() được sử dụng để deserialize một từ điển thuộc tính vào một thể hiện đối tượng. ModelSerializerimplements this method và tạo/cập nhật cá thể cho mô hình bạn đã chỉ định trong lớp Meta. Nếu số instance cho trước là None nghĩa là đối tượng vẫn phải được tạo, vì vậy bạn chỉ cần thêm thuộc tính parent_id vào đối số attrs và gọi super().

Vì vậy, theo cách này bạn không phải chỉ định trường chỉ đọc hoặc có chế độ xem/công cụ nối tiếp tùy chỉnh.thông tin

thêm: http://www.django-rest-framework.org/api-guide/serializers#declaring-serializers

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