1. 程式人生 > >Django REST framework+Vue 打造生鮮電商項目(筆記二)

Django REST framework+Vue 打造生鮮電商項目(筆記二)

開始 allow 動作 false 做到 category gef eric ocs

(轉自https://www.cnblogs.com/derek1184405959/p/8768059.html)(有修改)

接下來開始引入django resfulframework,體現它的強大之處

一、首先,按照以前的做法,如果我們想把商品列表打包成JSON數據顯示在首頁,就是

1、寫view函數

# goods/view.py

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)

2、配置url

from goods.view import GoodsListView

urlpatterns = [
   #商品列表頁
    path(goods/,GoodsListView.as_view(),name=
goods-list) ]

訪問http://127.0.0.1:8000/goods/ 可以獲取商品列表信息的json數據

技術分享圖片

二、django的serializer序列化model

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

# goods/view.py

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

但是這樣有個問題,就是ImageFieldFile 和add_time字段不能序列化

這時就要用到django的serializers

# goods/view_base.py

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()
       
        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) # 註意,這裏用的JsonResponse,這樣就不用json.dumps了

技術分享圖片

註意,這裏的圖片路徑是相對路徑。還有其中的goods_desc之所以會那樣是因為我們後臺在添加數據用的是富文本,不是文本框,因此可以上傳圖片啥的,保存起來就是這個效果。

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

  (1)字段序列化定死的,要想重組的話非常麻煩

  (2)從上面截圖可以看出來,images保存的是一個相對路徑,我們還需要補全路徑,而這些drf都可以幫助我們做到

以上寫了這麽多只是為了引入django rest framework和簡單介紹django的序列化用法,下面就是重點講解django rest framework了

三、3種方式實現商品列表頁

1、安裝djangorestframework、coreapi(drf的文檔支持)、django-guardian(drf對象級別的權限支持)

2、配置def文檔的url

# urls.py

from rest_framework.documentation import include_docs_urls

urlpatterns = [
    #drf文檔,title自定義
    path(docs,include_docs_urls(title=仙劍奇俠傳)),
]

3、配置rest_framework

#setting.py

INSTALLED_APPS = [
    rest_framework,
]
# urls.py

urlpatterns = [
    path(api-auth/,include(rest_framework.urls)),
]

4、第一種方法:APIview方式實現商品列表頁

(1)goods文件夾下面新建serializers.py

用drf的序列化實現商品列表頁展示,代碼如下:

# goods/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()

這裏的serializers.py可以理解成Django中在建立驗證表單Form,聯系這個知識點可以有助於理解。

(2)goods/views.py

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

技術分享圖片

(3)drf的Modelserializer實現商品列表頁

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

# goods/serializers.py

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__

技術分享圖片

外鍵category只顯示分類的id,如果我們也要把外鍵對應的內容也顯示出來。用serializers,它還可以嵌套使用,覆蓋外鍵字段

# goods/serializers.py

from rest_framework import serializers
from .models import Goods,GoodsCategory

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


class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = GoodsCategory
        fields = "__all__"


#ModelSerializer實現商品列表頁
class GoodsSerializer(serializers.ModelSerializer):
    #覆蓋外鍵字段
    category = CategorySerializer()
    class Meta:
        model = Goods
        fields = __all__

技術分享圖片

5、第二種方法:GenericView實現商品列表頁

(1)mixins和generic一起使用

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

用的時候需要定義queryset和serializer_class
GenericAPIView裏面默認為空

queryset = None

serializer_class = None
mixins裏的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)

更進一步的優化:

可以直接繼承ListAPIView,ListAPIView主要做了兩件事:

(1)ListAPIView(mixins.ListModelMixin,GenericAPIView) #繼承了這兩個類

(2)寫好了get方法

技術分享圖片

這下,我們要獲取商品列表頁的信息,只要寫三行代碼就可以了

class GoodsListView(generics.ListAPIView):
    商品列表頁
    queryset = Goods.objects.all()
    serializer_class = GoodsSerializer

6、第三種方法:viewsets和router完成商品列表頁

主要用到viewsets中的GenericViewSet

技術分享圖片

技術分享圖片

ViewSets和Routers結合使用

因為通過GenericViewSet中的viewSetMixin,我們不需要通過在views.py中寫函數來進行綁定,在router的幫助下,直接在urls.py中配置的時候來進行綁定,如下所示:

# urls.py

from goods.views import GoodsListViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()

#配置goods的url
router.register(rgoods, GoodsListViewSet)

urlpatterns = [
    #商品列表頁
    re_path(^, include(router.urls)),
]

# views.py

class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
    商品列表頁

    # 分頁
    pagination_class = GoodsPagination
    #這裏必須要定義一個默認的排序,否則會報錯
    queryset = Goods.objects.all().order_by(id)
    serializer_class = GoodsSerializer

如果不想用router的話,可改為:

# urls.py

goods_list = GoodsListViewSet.as_view(
    {get: list,}
)

7、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

8、添加分頁功能

先看rest_framework/settings.py源碼,裏面可以找到如何配置:比如認證、權限和分頁等等

技術分享圖片

添加分頁功能,配置如下:

REST_FRAMEWORK = {
    #分頁
    DEFAULT_PAGINATION_CLASS: rest_framework.pagination.PageNumberPagination,
    #每頁顯示的個數
    PAGE_SIZE: 10,
}

技術分享圖片

自定義分頁功能

from rest_framework.pagination import PageNumberPagination

class GoodsPagination(PageNumberPagination):
    ‘‘‘
    商品列表自定義分頁
    ‘‘‘
    #默認每頁顯示的個數
    page_size = 10
    #可以動態改變每頁顯示的個數
    page_size_query_param = page_size
    #頁碼參數
    page_query_param = page
    #最多能顯示多少頁
    max_page_size = 100


class GoodsListView(generics.ListAPIView):
    商品列表頁

    pagination_class = GoodsPagination    #分頁
    queryset = Goods.objects.all()
    serializer_class = GoodsSerializer

settings.py裏面就不用設置了

技術分享圖片

9、drf的request和response介紹

REST framework 的 Request 類擴展與標準的 HttpRequest,並做了相應的增強,比如更加靈活的請求解析(request parsing)和認證(request authentication)。

Request 解析

REST framwork 的 Request 對象提供了靈活的請求解析,允許你使用 JSON data 或 其他 media types 像通常處理表單數據一樣處理請求。

.data

request.data 返回請求主題的解析內容。這跟標準的 request.POSTrequest.FILES 類似,並且還具有以下特點:

  • 包括所有解析的內容,文件(file) 和 非文件(non-file inputs)。
  • 支持解析 POST 以外的 HTTP method , 比如 PUTPATCH
  • 更加靈活,不僅僅支持表單數據,傳入同樣的 JSON 數據一樣可以正確解析,並且不用做額外的處理(意思是前端不管提交的是表單數據,還是 JSON 數據,.data 都能夠正確解析)。

.data 具體操作,以後再說~

.query_params

request.query_params 等同於 request.GET,不過其名字更加容易理解。

為了代碼更加清晰可讀,推薦使用 request.query_params ,而不是 Django 中的 request.GET,這樣那夠讓你的代碼更加明顯的體現出 ----- 任何 HTTP method 類型都可能包含查詢參數(query parameters),而不僅僅只是 ‘GET‘ 請求。

.parser

APIView 類或者 @api_view 裝飾器將根據視圖上設置的 parser_classessettings 文件中的 DEFAULT_PARSER_CLASSES 設置來確保此屬性(.parsers)自動設置為 Parser 實例列表。

通常不需要關註該屬性......

如果你非要看看它裏面是什麽,可以打印出來看看,大概長這樣:

[<rest_framework.parsers.JSONParser object at 0x7fa850202d68>, <rest_framework.parsers.FormParser object at 0x7fa850202be0>, <rest_framework.parsers.MultiPartParser object at 0x7fa850202860>]

包含三個解析器 JSONParserFormParserMultiPartParser

註意: 如果客戶端發送格式錯誤的內容,則訪問 request.data 可能會引發 ParseError 。默認情況下, REST framework 的 APIView 類或者 @api_view 裝飾器將捕獲錯誤並返回 400 Bad Request 響應。 如果客戶端發送的請求內容無法解析(不同於格式錯誤),則會引發 UnsupportedMediaType 異常,默認情況下會被捕獲並返回 415 Unsupported Media Type 響應。

Responses

與基本的 HttpResponse 對象不同,TemplateResponse 對象保留了視圖提供的用於計算響應的上下文的詳細信息。直到需要時才會計算最終的響應輸出,也就是在後面的響應過程中進行計算。 — Django 文檔

REST framework 通過提供一個 Response 類來支持 HTTP 內容協商,該類允許你根據客戶端請求返回不同的表現形式(如: JSON ,HTML 等)。

Response 類的子類是 Django 的 SimpleTemplateResponseResponse 對象使用數據進行初始化,數據應由 Python 對象(native Python primitives)組成。然後 REST framework 使用標準的 HTTP 內容協商來確定它應該如何渲染最終響應的內容。

當然,您也可以不使用 Response 類,直接返回常規 HttpResponseStreamingHttpResponse 對象。 使用 Response 類只是提供了一個更好的交互方式,它可以返回多種格式。

除非由於某種原因需要大幅度定制 REST framework ,否則應該始終對返回 Response 對象的視圖使用 APIView 類或 @api_view 裝飾器。這樣做可以確保視圖執行內容協商,並在視圖返回之前為響應選擇適當的渲染器。

創建 response

Response()

與普通 HttpResponse 對象不同,您不會使用渲染的內容實例化 Response 對象。相反,您傳遞的是未渲染的數據,可能包含任何 Python 對象。

由於 Response 類使用的渲染器不能處理復雜的數據類型(比如 Django 的模型實例),所以需要在創建 Response 對象之前將數據序列化為基本的數據類型。

你可以使用 REST framework 的 Serializer 類來執行序列化的操作,也可以用自己的方式來序列化。

構造方法: Response(data, status=None, template_name=None, headers=None, content_type=None)

參數:

  • data: 響應的序列化數據。
  • status: 響應的狀態代碼。默認為200。
  • template_name: 選擇 HTMLRenderer 時使用的模板名稱。
  • headers: 設置 HTTP header,字典類型。
  • content_type: 響應的內容類型,通常渲染器會根據內容協商的結果自動設置,但有些時候需要手動指定。

屬性

.data

還沒有渲染,但已經序列化的響應數據。

.status_code

狀態碼

.content

將會返回的響應內容,必須先調用 .render() 方法,才能訪問 .content

.template_name

只有在 response 的渲染器是 HTMLRenderer 或其他自定義模板渲染器時才需要提供。

.accepted_renderer

用於將會返回的響應內容的渲染器實例。

從視圖返回響應之前由 APIView@api_view 自動設置。

.accepted_media_type

內容協商階段選擇的媒體類型。

從視圖返回響應之前由 APIView@api_view 自動設置。

.renderer_context

將傳遞給渲染器的 .render() 方法的附加的上下文信息字典。

從視圖返回響應之前由 APIView@api_view 自動設置。

標準 HttpResponse 屬性

Response 類擴展於 SimpleTemplateResponse,並且響應中也提供了所有常用的屬性和方法。例如,您可以用標準方式在響應中設置 header:

response = Response()
response[Cache-Control‘] = no-cache

.render()

與其他任何 TemplateResponse 一樣,調用此方法將響應的序列化數據呈現為最終響應內容。響應內容將設置為在 accepted_renderer 實例上調用 .render(data,accepted_media_type,renderer_context) 方法的結果。

通常不需要自己調用 .render() ,因為它是由 Django 處理的。

Django REST framework+Vue 打造生鮮電商項目(筆記二)