2013-08-19 27 views
13

Trong khung công tác REST Django, những gì có liên quan trong việc tạo ra một biểu diễn nối tiếp phẳng, đọc-ghi? Các tài liệu đề cập đến một 'đại diện phẳng' (kết thúc của phần http://django-rest-framework.org/api-guide/serializers.html#dealing-with-nested-objects) nhưng không cung cấp các ví dụ hoặc bất cứ điều gì ngoài một đề nghị để sử dụng một lớp con RelatedField.Khuôn khổ Django REST phẳng, đọc-ghi serializer

Ví dụ: cách cung cấp đại diện phẳng cho mối quan hệ UserUserProfile, bên dưới?

# Model 
class UserProfile(models.Model): 
    user = models.OneToOneField(User) 
    favourite_number = models.IntegerField() 

# Serializer 
class UserProfileSerializer(serializers.ModelSerializer): 
    email = serialisers.EmailField(source='user.email') 
    class Meta: 
     model = UserProfile 
     fields = ['id', 'favourite_number', 'email',] 

Trên đây UserProfileSerializer không cho phép bằng văn bản cho lĩnh vực email, nhưng tôi hy vọng nó thể hiện ý định đủ tốt. Vì vậy, làm thế nào nên một "phẳng" đọc-ghi serializer được xây dựng để cho phép một thuộc tính email ghi trên UserProfileSerializer? Có thể làm điều này khi subclassing ModelSerializer?

Cảm ơn.

Trả lời

6

Nhìn vào nguồn khung REST Django (DRF) Tôi đã giải quyết trên quan điểm rằng một bộ nối tiếp DRF được gắn chặt với một Mô hình đi kèm cho các mục đích unserializing. Field 's source param làm cho điều này ít hơn cho mục đích serializing.

Với ý nghĩ đó, và xem serializers như đóng gói xác nhận và lưu hành vi (ngoài của họ (un) hành vi serializing) Tôi sử dụng hai serializers: một cho từng mô hình sử dụng và UserProfile:

class UserSerializer(serializer.ModelSerializer): 
    class Meta: 
     model = User 
     fields = ['email',] 

class UserProfileSerializer(serializer.ModelSerializer): 
    email = serializers.EmailField(source='user.email') 
    class Meta: 
     model = UserProfile 
     fields = ['id', 'favourite_number', 'email',] 

Thông số source trên EmailField xử lý trường hợp tuần tự đầy đủ (ví dụ: khi phục vụ yêu cầu GET). Đối với unserializing (ví dụ như khi serivicing yêu cầu PUT) nó là cần thiết để làm một công việc rất ít trong giao diện, kết hợp xác nhận và lưu hành vi của hai serializers:

class UserProfileRetrieveUpdate(generics.GenericAPIView): 
    def get(self, request, *args, **kwargs): 
     # Only UserProfileSerializer is required to serialize data since 
     # email is populated by the 'source' param on EmailField. 
     serializer = UserProfileSerializer(
       instance=request.user.get_profile()) 
     return Response(serializer.data) 

    def put(self, request, *args, **kwargs): 
     # Both UserProfileSerializer and UserProfileSerializer are required 
     # in order to validate and save data on their associated models. 
     user_profile_serializer = UserProfileSerializer(
       instance=request.user.get_profile(), 
       data=request.DATA) 
     user_serializer = UserSerializer(
       instance=request.user, 
       data=request.DATA) 
     if user_profile_serializer.is_valid() and user_serializer.is_valid(): 
      user_profile_serializer.save() 
      user_serializer.save() 
      return Response(
        user_profile_serializer.data, status=status.HTTP_200_OK) 
     # Combine errors from both serializers. 
     errors = dict() 
     errors.update(user_profile_serializer.errors) 
     errors.update(user_serializer.errors) 
     return Response(errors, status=status.HTTP_400_BAD_REQUEST) 
+0

Paul, là request.DATA của bạn ở đây một mảng lồng nhau JSON duy nhất, hoặc bạn có một cho mỗi mô hình trong yêu cầu POST? (Đang cố gắng để đạt được một cái gì đó tương tự) – jvc26

+0

@ jvc26, ví dụ của tôi ở trên sẽ sử dụng một đối tượng JSON gốc duy nhất trong 'request.DATA'. 'request.DATA' sẽ trông giống như sau: ' {'id': '1', 'favourite_number': '2', 'email': '[email protected]'} ' Theo như khách hàng có liên quan, đối tượng JSON đó đại diện cho một cá thể mô hình duy nhất và sẽ không có kiến ​​thức về hai mô hình ('User' và' UserProfile') mà nó thực sự phân chia thành trên máy chủ. 'UserSerializer' và' UserProfileSerializer' được sử dụng để trích xuất, xác thực và lưu nội dung từ request.DATA cho các mô hình liên quan của chúng. –

2

Đầu tiên: xử lý tốt hơn các ghi lồng nhau là theo cách của nó.

Thứ hai: Các Serializer Relations docs nói của cả hai PrimaryKeyRelatedFieldSlugRelatedField rằng "Theo mặc định lĩnh vực này được đọc-viết ..." - vì vậy nếu trường email của bạn là duy nhất (is it?) Nó có thể là bạn có thể sử dụng SlugRelatedField và nó sẽ chỉ hoạt động - tôi chưa thử điều này (tuy nhiên).

Thứ ba: Thay vào đó tôi đã sử dụng một lớp con đơn giản Field sử dụng số source="*" technique để chấp nhận toàn bộ đối tượng. Từ đó, tôi kéo trường liên quan theo cách thủ công theo số to_native và trả lại trường đó - đây là chỉ đọc. Để viết tôi đã kiểm tra request.DATA trong post_save và cập nhật các đối tượng liên quan ở đó - Đây không phải là tự động nhưng nó hoạt động.

Vì vậy, Thứ tư: Nhìn vào những gì bạn đã có, cách tiếp cận của tôi (ở trên) số tiền để đánh dấu lĩnh vực email của bạn như là read-only và sau đó thực hiện post_save để kiểm tra một giá trị email và tiến hành cập nhật cho phù hợp.

+0

Cảm ơn, @ carton-gibson. (1) Tôi sẽ mong được viết dễ dàng hơn trên các biểu diễn lồng nhau. (2) Ví dụ của tôi được đơn giản hóa để minh hoạ, nhưng đề xuất của bạn là thú vị. (3 & 4) Làm một số post_save() fiddling đã xảy ra với tôi, và tôi đã sử dụng trong các tình huống khác, nhưng sử dụng hai serializers (câu trả lời của riêng tôi) có vẻ sạch hơn và mạnh mẽ hơn. –

0

Mặc dù điều này không trả lời đúng các câu hỏi - Tôi nghĩ nó sẽ giải quyết nhu cầu của bạn. Vấn đề có thể nhiều hơn trong sự phân chia của hai mô hình để đại diện cho một thực thể hơn là một vấn đề với DRF.

Kể từ Django 1.5, bạn có thể làm cho một người sử dụng tùy chỉnh, nếu tất cả các bạn muốn là một số phương pháp và trường bổ sung nhưng ngoài việc đó bạn đang hạnh phúc với người sử dụng Django, sau đó tất cả các bạn cần làm là:

class MyUser(AbstractBaseUser): favourite_number = models.IntegerField()

và trong cài đặt: AUTH_USER_MODEL = 'myapp.myuser'

(Và dĩ nhiên là di chuyển db, có thể được thực hiện khá đơn giản bằng cách sử dụng tùy chọn db_table để trỏ đến bảng người dùng hiện tại của bạn và chỉ thêm cột mới tại đó).

Sau đó, bạn có trường hợp phổ biến mà DRF vượt trội.