zoukankan      html  css  js  c++  java
  • drf序列化器serializers.SerializerMethodField()的用法

    问题描述:

    为什么DRF中有时候返回的json中图片是带域名的,有时候是不带域名的呢?

    解析:

    带域名的结果是在view中对模型类序列化的,DRF在序列化图片的时候 会检查上下文有没有request,如果有,就给图片加上域名,

    比如说我们视图用的是apiview():

    我们需要序列化数据的时候,加  context={"request":request}

    TestSerilaizer(instance=instance, context={"request":request})

    然后这样序列化器就取到了request对象,然后你再测试下图片url即可

    还有一种情况,在序列化器里调用序列化器的时候,也会碰到这种情况,当然也必须要这样解决,

    goods_json = GoodsSerializer(good_ins, many=False, context={'request': self.context['request']}).data    #注意具体语法


    我再说几种传参的方法,这几种方法在开发过程中也会经常的遇到,

    在提供序列化器对象的时候,REST framework会向对象的context属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器时使用。

    serializer = AccountSerializer(account, context={'request': request}) 

    解析,如果是apiview,你视图函数里就写第一行格式,确保序列化器里可以取到requsts对象,当然如果视图函数继承的非apiview,那么可以忽略,

    我们可以在序列化器里取很多参数方便我们使用,如

        def validate(self, attrs):#在提供序列化器对象的时候,REST framework会向对象的context属性补充三个数据:
            # request、format、view,这三个数据对象可以在定义序列化器时使用
            mobile = self.context['view'].kwargs['mobile']   

    这样就可以取到url里的mobile参数,来我们接着看,



    在视图里这样取也可以,比如当我们重写视图的get方法时:我们这样取pk得值

    from rest_framework.generics import ListAPIView
    class NewGoodsView(ListAPIView):
        serializer_class = GoodeInfoSerializer
        queryset = GoodInfo.objects.all()
        def get(self,request,*args,**kwargs):
            goods = GoodInfo.objects.filter(type_id=kwargs['pk']).order_by('-id')[:2]
            ser = self.get_serializer(goods,many=True)
            return Response(ser.data)

    然后看看下面这张截图,当访问商品详情时 点击量+1,



    然后再继续看看,只要是我们请求后台时前端请求header头中携带token认证,然后我们在后台就可以用request 取到当前的用户对象,如

    class MyCartInfoView(ListAPIView):
        '''
        我的购物车页面
        '''
        serializer_class = CartInfoSerializer
        queryset = Cart.objects.all()
        def get_queryset(self):
            return self.queryset.filter(user = self.request.user,selected=True)

    下面,我再介绍四种我们经常会用到的方法

    get_objects() :

    接着看下面,我们在URL中并没有携带参数,这样的话视图找不到PK会抛出异常,当我们重写get_objects() 方法后,

    class UserDetalView(RetrieveAPIView):
        '''
        个人中心信息展示
        '''
        serializer_class = UserDetailSerializer
        permission_classes = (IsAuthenticated,)
        # 重写get_object方法,返回用户指定信息
        def get_object(self):
            return self.request.user

    解析:get_objects() 

         返回详情视图所需的模型类数据对象,默认使用lookup_field参数来过滤queryset。 在视图中可以调用该方法获取详情信息的模型类对象。

    若详情访问的模型类对象不存在,会返回404。

    get_queryset():

    返回视图使用的查询集,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写,例如:

     

    get_serializer(self, args, *kwargs)
    返回序列化器对象,被其他视图或扩展类使用,如果我们在视图中想要获取序列化器对象,可以直接调用此方法。

    class CartCountView(GenericAPIView):
        '''
        detail页面购物车数量渲染
        '''
        serializer_class = CartCountSerializer
        queryset = Cart.objects.all()
        def get(self,request):
            ser = self.get_serializer(self.get_queryset().filter(user=request.user),many=True)
            count = len(ser.data)
            return Response({'data':count})

    get_serializer_class(self)

    返回序列化器类,默认返回serializer_class,可以重写,例如:三级联动的实现,

    先看看序列化器,

    class AreaSerializer(serializers.ModelSerializer):
        '''
        行政区信息序列化器
        '''
        class Meta:
            model = Area
            fields = ('id', 'name')
    
    class SubAreaSerializer(serializers.ModelSerializer):
        '''
        子集行政区信息序列化器
        '''
        subs = AreaSerializer(read_only=True,many=True)
        class Meta:
            model = Area
            fields = ('id', 'name', 'subs')

    来看看视图,

    class AreaViewSet(CacheResponseMixin,ReadOnlyModelViewSet):
        # 关闭分页,因为高级视图集默认是有分页操作的,而我们这里前端页面选择省市县,是不需要分页的
        pagination_class = None
        # queryset = Area.objects.all()
        # serializer_class = AreaSerializer
    
        '''
        list:返回所有的省份信息 
        retrieve:返回特定省或市下的下属城市
        '''
        def get_queryset(self):
            '''
            返回视图使用的查询集,
            是列表视图与详情视图获取数据的基础,
            默认返回queryset属性
            '''
            if self.action == 'list':
                return Area.objects.filter(parent=None)
            else:
                return Area.objects.all()
    
        def get_serializer_class(self):
            if self.action == 'list':
                return AreaSerializer
            else:
                return SubAreaSerializer

    来看看url

    from django.conf.urls import url
    from . import views
    from rest_framework.routers import DefaultRouter
    router = DefaultRouter()
    router.register('areas',views.AreaViewSet,base_name='area')
    
    urlpatterns = [
    
    ]
    
    urlpatterns += router.urls


    其实能坚持看到文章末尾的小同学们,你会发现末尾有彩蛋,

    drf模型序列化器默认仅返回数据库中已存在字段,如果想新增输出字段,改如何操作?

    例如:输出用户角色时,顺便输出当前角色总共有多少用户.

    先举个例子:

    class Role(models.Model):
        """角色表,一的一方"""
        name = models.CharField(max_length=30, unique=True, verbose_name='角色名称')  # 媒体运营,广告运营,活动运营,财务,技术,唯一,必填
        desc = models.CharField(max_length=100, null=True, blank=True, verbose_name='角色描述')  # 非必填
     
        class Meta:
            db_table = 'tb_role'
            verbose_name = '角色'
            verbose_name_plural = verbose_name
     
        def __str__(self):
            """控制对象输出内容"""
            return self.name
     
     
    class User(BaseModel):
        """用户表,多的一方"""
        account = models.CharField(max_length=30, unique=True, verbose_name='登录账户')  # 必填,唯一
        password = models.CharField(max_length=100, null=True, blank=True, default='888888',
                                    verbose_name='登录密码')  # 非必填,默认888888,长度100是为了以后加密扩展
        username = models.CharField(max_length=30, null=True, blank=True, verbose_name='用户名称')  # 非必填
        role = models.ForeignKey(Role, on_delete=models.CASCADE, related_name='user', verbose_name='角色')
     
        class Meta:
            db_table = 'tb_user'
            verbose_name = '用户'
            verbose_name_plural = verbose_name
     
        def __str__(self):
            """控制对象输出内容"""
            return self.account

    接着看序列化器,

    class RoleModelSerializer(serializers.ModelSerializer):
        """角色模型序列化器"""
        user_count = serializers.SerializerMethodField(label='用户数量')  # 新增数据库不存在字段用户数量
     
        class Meta:
            model = Role
            fields = ['id', 'name', 'desc', 'user_count']
     
        def get_user_count(self, obj):
            """
            返回当前角色用户数量
            固定写法,obj代表Role实例对象,模型类配置了反向引用user代表当前角色用户
            """
            number = obj.user.count()
            return number

    注意: 

    user_count 字段在数据库中不能存在,下面写方法的时候前面加 get_  就可以,这样就得到我们需要的数据了。
    在此方法里需要调用序列化器,我们直接调用即可,举例:
        ad_goods = serializers.SerializerMethodField()  #位于中间部分goods商品的img大图片显示
    
        def get_ad_goods(self, obj):
            goods_json = {}
            ad_goods = IndexAd.objects.filter(category_id=obj.id,)  #过滤goods在广告表中的数据
            if ad_goods:
                good_ins = ad_goods[0].goods  #取一条
                goods_json = IndexGoodsSerializer(good_ins, many=False, context={'request': self.context['request']}).data
            return goods_json

    HiddenField()

    HiddenField的值不依靠输入,而需要设置默认的值,不需要用户自己post数据过来,也不会显式返回给用户,最常用的就是user!!

    我们在登录情况下,进行一些操作,假设一个用户去收藏了某一门课,那么后台应该自动识别这个用户,然后用户只需要将课程的id post过来,那么这样的功能,我们配合CurrentUserDefault()实现。

    下面是一个用户留言功能的实现:

    class LeavingMessageSerializer(serializers.ModelSerializer):
        '''
        用户留言
        '''
        # 获取当前登录的用户
        user = serializers.HiddenField(
            default=serializers.CurrentUserDefault()
        )
        #read_only:只返回,post时候可以不用提交,format:格式化输出
        add_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M')
        class Meta:
            model = UserLeavingMessage
            fields = ("user", "message_type", "subject", "message", "file", "id" ,"add_time")


    大家有什么问题,尽可以在下方留言。
  • 相关阅读:
    Java 泛型的使用
    IDEA 2017.2.2 环境下使用JUnit
    Tomcat的下载安装及使用
    JavaScript基础知识(二)
    JavaScript基础知识(一)
    CSS3基础(4)——CSS3 渲染属性
    CSS3基础(3)——CSS3 布局属性全接触
    CSS3基础(2)—— 文字与字体相关样式、盒子类型、背景与边框相关样式、变形处理、动画功能
    CSS3 基础(1)——选择器详解
    CSS基础学习笔记
  • 原文地址:https://www.cnblogs.com/lvye001/p/10219137.html
Copyright © 2011-2022 走看看