2016-12-24 30 views
6

Tôi đang gặp phải một vấn đề nhỏ ngay bây giờ với Django Rest Framework. Tôi đang cố gắng để đăng một đối tượng với các đối tượng lồng nhau trong đó.Django Rest Framework POST đối tượng lồng nhau

Dưới đây là tôi serializers.py:

class ClassSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = Class 
     fields = ('number', 'letter') 


class SubjectSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = Subject 
     fields = ('title',) 


class ExamSerializer(serializers.ModelSerializer): 
    subject = SubjectSerializer() 
    clazz = ClassSerializer() 

    class Meta: 
     model = Exam 
     fields = ('id', 'subject', 'clazz', 'topic', 'date', 'details') 
     depth = 1 

    def create(self, validated_data): 
     return Exam.objects.create(**validated_data) 

    def update(self, instance, validated_data): 
     instance.__dict__.update(**validated_data) 
     instance.save() 

     return instance 

create() từ views.py:

def create(self, request): 
    serializer = self.serializer_class(data=request.data) 
    serializer.is_valid(raise_exception=True) 
    self.perform_create(serializer) 

    return Response(serializer.validated_data, status=status.HTTP_201_CREATED) 

Và ở đây nó là phản ứng từ Postman: Postman response

Tôi đã đọc một số đăng bài ở đây về vấn đề này nhưng tôi cho đến khi bị mắc kẹt với nó. Tôi đã cố khắc phục nó theo nhiều cách nhưng nó vẫn đang trả về "This field is required.".

+0

Đó là một vấn đề thường gặp, nhìn vào câu trả lời của tôi, bạn sẽ thấy nó hữu ích. http://stackoverflow.com/questions/41308406/django-rest-framework-add-object-to-request-data-and-then-call-serializer-is-va –

Trả lời

15

Bạn đang xử lý sự cố của nested serialization. Vui lòng đọc tài liệu được liên kết trước khi tiếp tục.

Câu hỏi của bạn liên quan đến một khu vực phức tạp của các vấn đề trong DRF và do đó yêu cầu một số giải thích và thảo luận để hiểu cách hoạt động của bộ nối tiếp và bộ đếm.

Tôi sẽ thảo luận vấn đề đại diện cho dữ liệu SubjectClass của bạn thông qua cùng một điểm cuối bằng cách sử dụng biểu diễn dữ liệu khác nhau cho các phương thức HTTP khác nhau, vì đây thường là vấn đề khi mọi người muốn đại diện cho dữ liệu của họ ở định dạng lồng nhau; họ muốn cung cấp cho người dùng giao diện đủ thông tin để sử dụng rõ ràng, ví dụ: thông qua bộ chọn thả xuống.

Theo mặc định Django và Django REST Framework (DRF) tham chiếu đến các đối tượng liên quan (SubjectClass) của bạn khóa chính. Những, theo mặc định, là tự động tăng số nguyên phím với Django. Nếu bạn muốn tham khảo chúng bằng những cách khác, bạn phải viết ghi đè cho điều này. Có một vài tùy chọn khác nhau.

  1. tùy chọn đầu tiên là để chuyên tạo và cập nhật logic của bạn: Tham khảo lớp học của bạn thông qua một số thuộc tính khác (s) và viết bằng tay tra cứu để tạo cho mình, hoặc thiết lập các phím mà bạn đang đề cập đến như là primary key lớp học của bạn. Bạn có thể đặt tên lớp, UUID hoặc bất kỳ thuộc tính nào khác làm khóa cơ sở dữ liệu chính, miễn là nó là duy nhất, single field (lý do tôi đề cập đến điều này là vì hiện tại bạn đang tìm kiếm các mô hình Class của mình với tìm kiếm tổng hợp bao gồm cụm từ tìm kiếm tổng hợp (số, chữ cái). Bạn có thể ghi đè lên các tra cứu đối tượng liên quan trong phương thức xem create (cho POST), ví dụ, nhưng bạn sẽ phải xử lý các tra cứu tương tự trong phương thức xem update của bạn (đối với PUT và PATCH).
  2. Thứ hai, theo ý kiến ​​của tôi tùy chọn thích hợp hơn, là để chuyên đại diện đối tượng của bạn: Tham khảo các lớp học của bạn bình thường qua khóa chính và tạo một serializer để đọc đối tượng và một cho việc tạo và cập nhật nó. Điều này có thể dễ dàng đạt được bằng cách thừa kế lớp serializer và ghi đè các biểu diễn của bạn. Sử dụng khóa chính trong POST, PUT, PATCH, v.v.yêu cầu cập nhật tham chiếu lớp học và khóa ngoại của bạn.

Lựa chọn 1: Nhìn Lớp và đối tượng lên với một thuộc tính tùy ý trong tạo và cập nhật:

Đặt serializers lớp lồng nhau của bạn như là read-only:

class ExamSerializer(serializers.ModelSerializer): 
    subject = SubjectSerializer(read_only=True) 
    clazz = ClassSerializer(read_only=True) 

Override tạo của khung nhìn của bạn để tìm kiếm các lớp liên quan trên các thuộc tính dạng tự do. Ngoài ra, hãy xem how DRF implements this with mixins. Bạn cũng sẽ phải ghi đè phương pháp update bạn để xử lý những cách chính xác, và đưa vào tài khoản PATCH (cập nhật từng phần) hỗ trợ ngoài PUT (cập nhật) nếu bạn chọn cách này:

def create(self, request): 
    # Look up objects by arbitrary attributes. 
    # You can check here if your students are participating 
    # the classes and have taken the subjects they sign up for. 
    subject = get_object_or_404(Subject, title=request.data.get('subject')) 
    clazz = get_object_or_404(
     Class, 
     number=request.data.get('clazz_number') 
     letter=request.data.get('clazz_letter') 
    ) 

    serializer = self.get_serializer(data=request.data) 
    serializer.is_valid(raise_exception=True) 
    serializer.save(clazz=clazz, subject=subject) 
    headers = self.get_success_headers(serializer.data) 

    return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) 

Lựa chọn 2 : Chuyên môn hóa công cụ nối tiếp của bạn để đọc và viết và sử dụng các khóa chính; Đây là phương pháp thành ngữ:

Đầu tiên xác định một ModelSerializer mặc định bạn muốn sử dụng cho các hoạt động bình thường (POST, PUT, PATCH):

class ExamSerializer(serializers.ModelSerializer) 
    class Meta: 
     model = Exam 
     fields = ('id', 'subject', 'clazz', 'topic', 'date', 'details') 

Sau đó ghi đè lên các lĩnh vực cần thiết với các loại đại diện bạn muốn để cung cấp cho họ để đọc dữ liệu (GET):

class ExamReadSerializer(ExamSerializer): 
    subject = SubjectSerializer(read_only=True) 
    clazz = ClassSerializer(read_only=True) 

Sau đó specify the serializer you wish to use for different operations cho ViewSet của bạn. Ở đây chúng ta trả lại dữ liệu Đối tượng và lớp lồng nhau cho các hoạt động đọc, nhưng chỉ sử dụng các phím chính của họ cho các hoạt động cập nhật (đơn giản hơn nhiều):

class ExamViewSet(viewsets.ModelViewSet): 
    queryset = Exam.objects.all() 

    def get_serializer_class(self): 
     # Define your HTTP method-to-serializer mapping freely. 
     # This also works with CoreAPI and Swagger documentation, 
     # which produces clean and readable API documentation, 
     # so I have chosen to believe this is the way the 
     # Django REST Framework author intended things to work: 
     if self.request.method in ('GET',) 
      # Since the ReadSerializer does nested lookups 
      # in multiple tables, only use it when necessary 
      return ExamReadSerializer 
     return ExamSerializer 

Như bạn có thể thấy, phương án 2 có vẻ khá ít phức tạp và dễ bị lỗi, chỉ chứa 3 dòng mã viết tay trên đầu trang của DRF (việc thực hiện get_serializer_class). Chỉ cần để logic của khung công tác tìm ra các biểu diễn và tạo và cập nhật các đối tượng cho bạn.

Tôi đã thấy nhiều cách tiếp cận khác, nhưng cho đến nay đây là những phương pháp đã tạo ra mã ít nhất để duy trì cho tôi và tận dụng thiết kế của DRF một cách rõ ràng.

+0

Cảm ơn bạn rất nhiều, người đàn ông! Đó là một cách tiếp cận tốt mà tôi sẽ sử dụng cho các dự án tiếp theo. – wencakisa

+0

Tôi rất vui nếu nó giúp bạn ra ngoài :) Có một Xmas vui vẻ! –

+0

Giáng sinh vui vẻ! – wencakisa

0

Tôi đã gặp vấn đề tương tự khi cố đăng một đối tượng JSON lồng nhau lên DRF (Django Rest Framework).

Khi bạn đã thiết lập đúng cách viết serializers lồng nhau (xem tài liệu trên writable nested serializers), bạn có thể kiểm tra xem nó hoạt động bằng cách sử dụng browsable API và đăng/đặt dữ liệu ở đó. Nếu điều đó hoạt động và bạn vẫn nhận được "Trường này là bắt buộc" lỗi trên các mô hình lồng nhau của bạn khi đăng/đặt đối tượng JSON, bạn có thể phải đặt loại nội dung theo yêu cầu của mình.

This answer cung cấp giải pháp tôi cần và được tóm tắt bên dưới.

$.ajax ({ 
    // Other parameters e.g. url, type 
    data: JSON.stringify(data), 
    dataType: "json", 
    contentType: "application/json; charset=utf-8", 
}); 

Tôi cần đặt "contentType" cũng như "xâu chuỗi" đối tượng js của tôi.

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