1. 程式人生 > >從django的序列化到rest-framework 序列化

從django的序列化到rest-framework 序列化

1.利用Django的view實現返回json資料

from django.views.generic import View
from goods.models import Goods

class GoodsListView(View):
    def get(self,request):
        #通過django的view實現商品列表頁
        json_list = []
        #獲取所有商品
        goods = Goods.objects.all()
        for good in goods:
            json_dict 
= {} #獲取商品的每個欄位,鍵值對形式 json_dict['name'] = good.name json_dict['category'] = good.category.name json_dict['market_price'] = good.market_price json_list.append(json_dict) from django.http import HttpResponse import json
#返回json,一定要指定型別content_type='application/json' return HttpResponse(json.dumps(json_list),content_type='application/json')
View Code

2.django的serializer序列化model

(1)model_to_dict

當欄位比較多時,一個欄位一個欄位的提取很麻煩,可以用model_to_dict,將model整個轉化為dict

from django.views.generic import View
from
goods.models import Goods class GoodsListView(View): def get(self,request): #通過django的view實現商品列表頁 json_list = [] #獲取所有商品 goods = Goods.objects.all() # for good in goods: # json_dict = {} # #獲取商品的每個欄位,鍵值對形式 # json_dict['name'] = good.name # json_dict['category'] = good.category.name # json_dict['market_price'] = good.market_price # json_list.append(json_dict) from django.forms.models import model_to_dict for good in goods: json_dict = model_to_dict(good) json_list.append(json_dict) from django.http import HttpResponse import json #返回json,一定要指定型別content_type='application/json' return HttpResponse(json.dumps(json_list),content_type='application/json')
View Code
但是這樣有個問題,就是ImageField 和models.DateTimeField欄位不能序列化

如何才能將所有欄位序列化呢?就要用到django的serializers

(2)django serializer的用法

from django.views.generic import View
from goods.models import Goods

class GoodsListView(View):
    def get(self,request):
        #通過django的view實現商品列表頁
        json_list = []
        #獲取所有商品
        goods = Goods.objects.all()
        # for good in goods:
        #     json_dict = {}
        #     #獲取商品的每個欄位,鍵值對形式
        #     json_dict['name'] = good.name
        #     json_dict['category'] = good.category.name
        #     json_dict['market_price'] = good.market_price
        #     json_list.append(json_dict)

        import json
        from django.core import serializers
        from django.http import JsonResponse

        json_data = serializers.serialize('json',goods)
        json_data = json.loads(json_data)
        #In order to allow non-dict objects to be serialized set the safe parameter to False.
        return JsonResponse(json_data,safe=False)
View Code

django的serializer雖然可以很簡單實現序列化,但是有幾個缺點

  • 欄位序列化定死的,要想重組的話非常麻煩
  • 以上寫了這麼多隻是為了引入django rest framework和簡單介紹django的序列化用法,下面就是重點講解django rest framework了
 

APIview方式實現商品列表頁

配置rest_framework

settings.py中新增

INSTALLED_APPS = [
    'rest_framework',
]

MxShop/urls.py

urlpatterns = [
    path('api-auth/',include('rest_framework.urls')),
]
新建serializers.py
from rest_framework import serializers


class GoodsSerializer(serializers.Serializer):
    name = serializers.CharField(required=True,max_length=100)
    click_num = serializers.IntegerField(default=0)
    goods_front_image = serializers.ImageField()
View Code
# googd/views.py

from rest_framework.views import APIView
from goods.serializers import GoodsSerializer
from .models import Goods
from rest_framework.response import Response


class GoodsListView(APIView):
    '''
    商品列表
    '''
    def get(self,request,format=None):
        goods = Goods.objects.all()
        goods_serialzer = GoodsSerializer(goods,many=True)
        return Response(goods_serialzer.data)
View Code

一對多和多對多欄位的序列化:

class BookSerializers(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    price = serializers.IntegerField()
    pub_date = serializers.DateField()

    publish = serializers.CharField(source="publish.email")  # 一對多欄位使用source欄位處理

    # 多對多使用下面欄位,並且配一個方法,方法裡面可自定義任意東西,可取作者表的名字。
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        temp = []
        for obj in obj.authors.all():
            temp.append(obj.name)
        return temp
View Code

序列化的過程:

'''
序列化BookSerializers(book_list,many=True)過程:
    temp=[]
    for obj in book_list:
        temp.append({
            "title":obj.title,
            "price":obj.price,
            "pub_date":obj.pub_date,
            #"publish":str(publish),
            "publish":obj.publish.name,
            "authors":get_authors(obj)

        })
'''
View Code

drf的Modelserializer實現商品列表頁

上面是用Serializer實現的,需要自己手動新增欄位,如果用Modelserializer,會更加的方便,直接用__all__就可以全部序列化

from rest_framework import serializers
from .models import Goods

#Serializer實現商品列表頁
# class GoodsSerializer(serializers.Serializer):
#     name = serializers.CharField(required=True,max_length=100)
#     click_num = serializers.IntegerField(default=0)
#     goods_front_image = serializers.ImageField()

#ModelSerializer實現商品列表頁
class GoodsSerializer(serializers.ModelSerializer):
    class Meta:
        model = Goods
        fields = '__all__'
View Code

GenericView實現商品列表頁

1)mixins和generic一起用用

GenericAPIView繼承APIView,封裝了很多方法,比APIView功能更強大

class GenericAPIView(views.APIView):
    """
    Base class for all other generic views.
    """
    # You'll need to either set these attributes,
    # or override `get_queryset()`/`get_serializer_class()`.
    # If you are overriding a view method, it is important that you call
    # `get_queryset()` instead of accessing the `queryset` property directly,
    # as `queryset` will get evaluated only once, and those results are cached
    # for all subsequent requests.
    queryset = None
    serializer_class = None

    # If you want to use object lookups other than pk, set 'lookup_field'.
    # For more complex lookup requirements override `get_object()`.
    lookup_field = 'pk'
    lookup_url_kwarg = None

    # The filter backend classes to use for queryset filtering
    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS

    # The style to use for queryset pagination.
    pagination_class = api_settings.DEFAULT_PAGINATION_CLASS

    def get_queryset(self):
        """
        Get the list of items for this view.
        This must be an iterable, and may be a queryset.
        Defaults to using `self.queryset`.

        This method should always be used rather than accessing `self.queryset`
        directly, as `self.queryset` gets evaluated only once, and those results
        are cached for all subsequent requests.

        You may want to override this if you need to provide different
        querysets depending on the incoming request.

        (Eg. return a list of items that is specific to the user)
        """
        assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )

        queryset = self.queryset
        if isinstance(queryset, QuerySet):
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all()
        return queryset

    def get_object(self):
        """
        Returns the object the view is displaying.

        You may want to override this if you need to provide non-standard
        queryset lookups.  Eg if objects are referenced using multiple
        keyword arguments in the url conf.
        """
        queryset = self.filter_queryset(self.get_queryset())

        # Perform the lookup filtering.
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)

        # May raise a permission denied
        self.check_object_permissions(self.request, obj)

        return obj

    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)

    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.

        You may want to override this if you need to provide different
        serializations depending on the incoming request.

        (Eg. admins get full serialization, others get basic serialization)
        """
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )

        return self.serializer_class

    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        return {
            'request': self.request,
            'format': self.format_kwarg,
            'view': self
        }

    def filter_queryset(self, queryset):
        """
        Given a queryset, filter it with whichever filter backend is in use.

        You are unlikely to want to override this method, although you may need
        to call it either from a list view, or from a custom `get_object`
        method if you want to apply the configured filtering backend to the
        default queryset.
        """
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

    @property
    def paginator(self):
        """
        The paginator instance associated with the view, or `None`.
        """
        if not hasattr(self, '_paginator'):
            if self.pagination_class is None:
                self._paginator = None
            else:
                self._paginator = self.pagination_class()
        return self._paginator

    def paginate_queryset(self, queryset):
        """
        Return a single page of results, or `None` if pagination is disabled.
        """
        if self.paginator is None:
            return None
        return self.paginator.paginate_queryset(queryset, self.request, view=self)

    def get_paginated_response(self, data):
        """
        Return a paginated style `Response` object for the given output data.
        """
        assert self.paginator is not None
        return self.paginator.get_paginated_response(data)
GenericAPIView原始碼
用的時候需要定義queryset和serializer_class
GenericAPIView裡面預設為空
  • queryset = None
  • serializer_class = None

ListModelMixin裡面list方法幫我們做好了分頁和序列化的工作,只要呼叫就好了

實現如下:

from goods.serializers import GoodsSerializer
from .models import Goods
from rest_framework.response import Response
from rest_framework import mixins
from rest_framework import generics


class GoodsListView(mixins.ListModelMixin,generics.GenericAPIView):
    '商品列表頁'
    queryset = Goods.objects.all()
    serializer_class = GoodsSerializer

    def get(self,request,*args,**kwargs):
        return self.list(request,*args,**kwargs)
View Code

上面的程式碼優化,可以直接繼承ListAPIView,ListAPIView主要做了兩件事:

  • ListAPIView(mixins.ListModelMixin,GenericAPIView)        繼承了這兩個類
  • 寫好了get方法
  • 我們要獲取商品列表頁的資訊,只要寫三行程式碼就可以了
  • class GoodsListView(generics.ListAPIView):
        '商品列表頁'
        queryset = Goods.objects.all()
        serializer_class = GoodsSerializer

    viewsets和router完成商品列表頁

  • 主要用到viewsets中的GenericViewSet
  • ViewSets和Routers結合使用
  • from goods.views import GoodsListViewSet
    from rest_framework.routers import DefaultRouter
    
    router = DefaultRouter()
    
    #配置goods的url
    router.register(r'goods', GoodsListViewSet)
    
    urlpatterns = [
        #商品列表頁
        url('^', include(router.urls)),
    ]
    複製程式碼
    url
    class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
        '商品列表頁'
    
        # 分頁
        pagination_class = GoodsPagination
        #這裡必須要定義一個預設的排序,否則會報錯
        queryset = Goods.objects.all().order_by('id')
        serializer_class = GoodsSerializer
    View

    drf的APIView、GenericView、viewsets和router的原理分析

  • genericViewSet 是最高的一層

    往下

    GenericViewSet(viewsets)     ----drf

      GenericAPIView                  ---drf

        APIView                        ---drf

          View            ----django

    這些view功能的不同,主要的是有mixin的存在

     

    mixins總共有五種:

      CreateModelMixin

      ListModelMixin

      UpdateModelMixin

      RetrieveModelMixin

      DestoryModelMixin

以ListModelMixin為例:

 

如果不繼承ListModelMixin的話,就無法將get和商品的列表關聯起來,另外還有其中的分頁等等,都無法實現。

還有其它幾個mixin(增刪改查區域性),這些功能都是mixin做的

 

 我們一般都是用viewsets

ViewSet類與View類其實幾乎是相同的,但提供的是read或update這些操作,而不是get或put 等HTTP動作。同時,ViewSet為我們提供了預設的URL結構, 使得我們能更專注於API本身。

 Router提供了一種簡單,快速,整合的方式來定義一系列的urls