2015-03-09 19 views
28

Tôi có một vài trường trong mô hình người dùng của mình là các trường lựa chọn và tôi đang cố gắng tìm ra cách tốt nhất để triển khai thực hiện điều đó vào khung công tác Django Rest.Khung Django Rest với ChoiceField

Dưới đây là một số mã được đơn giản hóa để hiển thị những gì tôi đang làm.

# models.py 
class User(AbstractUser): 
    GENDER_CHOICES = (
     ('M', 'Male'), 
     ('F', 'Female'), 
    ) 

    gender = models.CharField(max_length=1, choices=GENDER_CHOICES) 


# serializers.py 
class UserSerializer(serializers.ModelSerializer): 
    gender = serializers.CharField(source='get_gender_display') 

    class Meta: 
     model = User 


# viewsets.py 
class UserViewSet(viewsets.ModelViewSet): 
    queryset = User.objects.all() 
    serializer_class = UserSerializer 

Về cơ bản những gì tôi đang cố gắng làm là có các phương thức get/post/put sử dụng giá trị hiển thị của trường lựa chọn thay vì mã, trông giống như JSON bên dưới.

{ 
    'username': 'newtestuser', 
    'email': '[email protected]', 
    'first_name': 'first', 
    'last_name': 'last', 
    'gender': 'Male' 
    // instead of 'gender': 'M' 
} 

Tôi sẽ làm như thế nào? Đoạn mã trên không hoạt động. Trước khi tôi có một cái gì đó như thế này làm việc cho GET, nhưng đối với POST/PUT nó đã cho tôi lỗi. Tôi đang tìm kiếm lời khuyên chung về cách làm điều này, có vẻ như nó sẽ là một cái gì đó phổ biến, nhưng tôi không thể tìm thấy ví dụ. Hoặc là tôi đang làm điều gì đó khủng khiếp sai.

+0

Bạn đã thử http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield – DAKZH

Trả lời

52

Django cung cấp Model.get_FOO_display phương pháp để có được giá trị "con người có thể đọc được" của một lĩnh vực:

class UserSerializer(serializers.ModelSerializer): 
    gender = serializers.SerializerMethodField() 

    class Meta: 
     model = User 

    def get_gender(self,obj): 
     return obj.get_gender_display() 

cho DRF mới nhất (3.6.3) - Phương pháp đơn giản nhất là:

gender = serializers.CharField(source='get_gender_display') 
+0

Điều gì sẽ xảy ra nếu bạn muốn chuỗi hiển thị của tất cả các lựa chọn có sẵn? –

+0

@ HåkenLid bạn cần phải gọi từng cái một theo một phương pháp mới. – levi

+3

Hóa ra khung còn lại cho thấy các lựa chọn nếu bạn sử dụng phương thức tùy chọn http trên một ModelViewSet, vì vậy tôi không cần phải tùy chỉnh bộ nối tiếp. –

11

Tôi khuyên bạn nên sử dụng django-models-utils với tùy chỉnh DRF serializer field

Mã mới thành:

# models.py 
from model_utils import Choices 

class User(AbstractUser): 
    GENDER = Choices(
     ('M', 'Male'), 
     ('F', 'Female'), 
    ) 

    gender = models.CharField(max_length=1, choices=GENDER, default=GENDER.M) 


# serializers.py 
from rest_framework import serializers 

class ChoicesField(serializers.Field): 
    def __init__(self, choices, **kwargs): 
     self._choices = choices 
     super(ChoicesField, self).__init__(**kwargs) 

    def to_representation(self, obj): 
     return self._choices[obj] 

    def to_internal_value(self, data): 
     return getattr(self._choices, data) 

class UserSerializer(serializers.ModelSerializer): 
    gender = ChoicesField(choices=User.GENDER) 

    class Meta: 
     model = User 

# viewsets.py 
class UserViewSet(viewsets.ModelViewSet): 
    queryset = User.objects.all() 
    serializer_class = UserSerializer 
+1

câu hỏi này đã được trả lời một thời gian dài trước đây với một giải pháp tích hợp đơn giản hơn nhiều. – awwester

+8

2 sự khác biệt: 1) ChoicesField có thể được tái sử dụng và 2) nó hỗ trợ các phiên bản cho trường "giới tính" không phải là chỉ đọc nữa – nicolaspanel

+0

Cho rằng các giá trị cho các lựa chọn là bản thân quá ngắn tôi sẽ chỉ sử dụng 'GENDER = Choices ('Nam', 'Nữ') 'và' mặc định = GENDER.Male', vì điều đó bỏ qua nhu cầu tạo trường serializer tùy chỉnh. – ergusto

5

Các giải pháp sau đây làm việc với bất kỳ lĩnh vực với sự lựa chọn, mà không cần phải chỉ định trong serializer một phương pháp tùy chỉnh cho mỗi:

from rest_framework import serializers 

class ChoicesSerializerField(serializers.SerializerMethodField): 
    """ 
    A read-only field that return the representation of a model field with choices. 
    """ 

    def to_representation(self, value): 
     # sample: 'get_XXXX_display' 
     method_name = 'get_{field_name}_display'.format(field_name=self.field_name) 
     # retrieve instance method 
     method = getattr(value, method_name) 
     # finally use instance method to return result of get_XXXX_display() 
     return method() 

Ví dụ:

đưa ra:

class Person(models.Model): 
    ... 
    GENDER_CHOICES = (
     ('M', 'Male'), 
     ('F', 'Female'), 
    ) 
    gender = models.CharField(max_length=1, choices=GENDER_CHOICES) 

sử dụng:

class PersonSerializer(serializers.ModelSerializer): 
    ... 
    gender = ChoicesSerializerField() 

nhận:

{ 
    ... 
    'gender': 'Male' 
} 

thay vì:

{ 
    ... 
    'gender': 'M' 
} 
6

Probalbly bạn cần một cái gì đó giống như một nơi nào đó trong util.py của bạn và nhập khẩu trong bất cứ serializers ChoiceFields có liên quan.

class ChoicesField(serializers.Field): 
    """Custom ChoiceField serializer field.""" 

    def __init__(self, choices, **kwargs): 
     """init.""" 
     self._choices = OrderedDict(choices) 
     super(ChoicesField, self).__init__(**kwargs) 

    def to_representation(self, obj): 
     """Used while retrieving value for the field.""" 
     return self._choices[obj] 

    def to_internal_value(self, data): 
     """Used while storing value for the field.""" 
     for i in self._choices: 
      if self._choices[i] == data: 
       return i 
     raise serializers.ValidationError("Acceptable values are {0}.".format(list(self._choices.values()))) 
4

3.1 có API mới được gọi là customizing field mapping.Tôi đã sử dụng nó để thay đổi bản đồ ChoiceField mặc định để ChoiceDisplayField:

import six 
from rest_framework.fields import ChoiceField 


class ChoiceDisplayField(ChoiceField): 
    def __init__(self, *args, **kwargs): 
     super(ChoiceDisplayField, self).__init__(*args, **kwargs) 
     self.choice_strings_to_display = { 
      six.text_type(key): value for key, value in self.choices.items() 
     } 

    def to_representation(self, value): 
     if value is None: 
      return value 
     return { 
      'value': self.choice_strings_to_values.get(six.text_type(value), value), 
      'display': self.choice_strings_to_display.get(six.text_type(value), value), 
     } 

class DefaultModelSerializer(serializers.ModelSerializer): 
    serializer_choice_field = ChoiceDisplayField 

Nếu Bạn sử dụng DefaultModelSerializer:

class UserSerializer(DefaultModelSerializer):  
    class Meta: 
     model = User 
     fields = ('id', 'gender') 

Bạn sẽ nhận được một cái gì đó như:

... 

"id": 1, 
"gender": { 
    "display": "Male", 
    "value": "M" 
}, 
... 
0

tôi thấy cách tiếp cận soup boy 's để trở nên tốt nhất. Mặc dù tôi muốn đề xuất kế thừa từ serializers.ChoiceField thay vì serializers.Field. Bằng cách này, bạn chỉ cần ghi đè phương thức to_representation và phần còn lại hoạt động như một ChoiceField thông thường.

class DisplayChoiceField(serializers.ChoiceField): 

    def __init__(self, *args, **kwargs): 
     choices = kwargs.get('choices') 
     self._choices = OrderedDict(choices) 
     super(DisplayChoiceField, self).__init__(*args, **kwargs) 

    def to_representation(self, obj): 
     """Used while retrieving value for the field.""" 
     return self._choices[obj] 
+0

Bạn có quá nhiều dấu gạch dưới gọi '' super'' trong '__init__' của bạn – joeb

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