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")


大家有什么問題,盡可以在下方留言。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM