前端传输{性别:1},后端返回{性别:男}
- 首先models内部创建类
class NbUserInfo(models.Model):
name =models.CharField(verbose_name="姓名",max_length=32)
age =models.IntegerField(verbose_name="年龄",)
gender =models.SmallIntegerField(verbose_name="性别",choices=((1,"男"),(2,"女")))
-
执行 makemigrations和 migrate 生成相关表
-
url中添加对应的path
urlpatterns = [
path('api/<str:version>/nb/', views.NbUserView.as_view()),
]
- 视图中编写视图类和序列化器类
class NbUserSerializer(serializers.ModelSerializer):
class Meta:
model = models.NbUserInfo
fields = "__all__"
extra_kwargs = {
"id": {"read_only": True},
}
class NbUserView(APIView):
def post(self, request,*args, **kwargs):
ser = NbUserSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
方法一:自定义Field字段,使能操作数据库,执行get_gender_display()
SerializerMethodField() 配合 get_XX钩子函数
class NbUserSerializer(serializers.ModelSerializer):
gender = serializers.SerializerMethodField()
class Meta:
model = models.NbUserInfo
fields = "__all__"
extra_kwargs = {
"id": {"read_only": True},
}
def get_gender(self, obj):
return obj.get_gender_display()
class NbUserView(APIView):
def post(self, request,*args, **kwargs):
ser = NbUserSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
结果:缺少gender,SerializerMethodField中默认read_only=True,使gender字段无法传值
{
"name": "gj",
"age": 123
}
#SerializerMethodField中默认read_only=True,使gender字段无法传值,无法通过校验
class SerializerMethodField(Field):
def __init__(self, method_name=None, **kwargs):
self.method_name = method_name
kwargs['source'] = '*'
kwargs['read_only'] = True
super().__init__(**kwargs)
SerializerMethodField()的实现原理
先执行get_attribute方法,再执行to_representation方法,在to_representation中返回序列化结果。因此,get_xxx可以作为钩子函数。
原SerializerMethodField只能返回前端数据,不会与数据库进行交互,不能执行get_gender_display方法,因此在IntegerField方法基础上,增加SerializerMethodField()的方法,从而能够执行get_gender_display方法。
class NbCharField(serializers.IntegerField):
def __init__(self, method_name=None, **kwargs):
self.method_name = method_name
super().__init__(**kwargs)
def bind(self,field_name,parent):
if self.method_name is None:
self.method_name = 'get_{field_name}'.format(field_name=field_name)
super().bind(field_name,parent)
def get_attribute(self,instance):
method =getattr(self.parent,self.method_name)
return method(instance)
def to_representation(self, value):
return str(value)
class NbUserSerializer(serializers.ModelSerializer):
# gender = serializers.SerializerMethodField()
gender = NbCharField()
class Meta:
model = models.NbUserInfo
fields = ['id','name','age','gender']
extra_kwargs = {
"id": {"read_only": True},
}
def get_gender(self, obj):
print(obj)
return obj.get_gender_display()
class NbUserView(APIView):
def post(self, request,*args, **kwargs):
ser = NbUserSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
方法二:修改serializers中的to_representation方法
因为序列化源码执行过程,一定会走serializers中的to_representation方法,将字段名和其对应的值放到ret中。所以在此之间,执行钩子方法,调用get_gender_display方法,改写值。同理,也可改写其它字段的值。
class SbUserSerializer(NbHookSerializer,serializers.ModelSerializer):
class Meta:
model = models.NbUserInfo
fields = ['id', 'name', 'age', 'gender']
extra_kwargs = {
"id": {"read_only": True},
}
def to_representation(self, instance):
ret = {}
fields = self._readable_fields
for field in fields:
if hasattr(self, f'nb_{field.field_name}'):
value = getattr(self, f'nb_{field.field_name}')(instance)
ret[field.field_name] = value
else:
try:
attribute = field.get_attribute(instance)
except SkipField:
continue
# We skip `to_representation` for `None` values so that fields do
# not have to explicitly deal with that case.
#
# For related fields with `use_pk_only_optimization` we need to
# resolve the pk value.
check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
if check_for_none is None:
ret[field.field_name] = None
else:
ret[field.field_name] = field.to_representation(attribute)
return ret
def nb_gender(self, obj):
return obj.get_gender_display()