前端传输{性别:1},后端返回{性别:男}

  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,"女")))
  1. 执行 makemigrations和 migrate 生成相关表

  2. url中添加对应的path

urlpatterns = [
    path('api/<str:version>/nb/', views.NbUserView.as_view()),
]
  1. 视图中编写视图类和序列化器类
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()

posted on 2024-06-25 22:53  gagagjgj  阅读(27)  评论(0编辑  收藏  举报

导航