Django+Vue打造購物網站(三)


商品列表頁

通過商品列表頁面來學習drf

django的view實現商品列表頁

在goods目錄下新建一個views_base.py文件,用來區分drf的view和Dajngo自帶的view的區別
利用Django的view實現返回json數據

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/9/20 下午 01:16
# @Author  : gao
# @File    : views_base.py


from django.views.generic.base 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')

配置url

    path('goods/', GoodsListView.as_view(), name='goods'),

通過瀏覽器,可以獲取商品列表信息的json數據

好像還可以,這里繼續添加數據

json_dict["add_time"] = good.add_time

瀏覽器訪問

我們會發現報錯了,這種方法是行不通的

django的serializer序列化model

model_to_dict

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

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

打開瀏覽器訪問

發現依然報錯,ImageFieldFile 和add_time字段不能序列化
這種方法依然有局限性

django serializer
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)
        return JsonResponse(json_data, safe=False)


看着效果挺不錯的,數據都加載進來了,但是缺點也挺明顯的

  1. 字段是寫死的,不靈活
  2. image字段不完整

這些缺點drf都可以幫我們來完成

drf實現列表頁

安裝插件

pip install coreapi                         drf的文檔支持
pip install django-guardian           drf對象級別的權限支持
APIview方式實現商品列表頁

配置urls

    path('api-auth/',include('rest_framework.urls')),
    path('docs/',include_docs_urls(title='生鮮超市')),

配置rest_framework

INSTALLED_APPS = [
    'rest_framework',
]

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

goods/views.py

from rest_framework.response import Response
from rest_framework.views import APIView

from goods.models import Goods
from goods.serializers import GoodsSerializer


class GoodsListView(APIView):
    '''
    商品列表
    '''

    def get(self, request, format=None):
        goods = Goods.objects.all()
        goods_serialzer = GoodsSerializer(goods, many=True)
        return Response(goods_serialzer.data)

修改urls的GoodsListView的引入
瀏覽器訪問

這是drf渲染的界面
可以看到image字段已經幫我們補全了

drf的Modelserializer實現商品列表頁

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

from rest_framework import serializers

from goods.models import Goods


# 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__'


外鍵被序列化為id,如果想要顯示外鍵字段的信息,可以使用Serialzer的嵌套功能
serializers.py

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


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

    class Meta:
        model = Goods
        fields = '__all__'


樂意看到,category字段顯示的已經是詳細信息了,不再是一個id了

GenericView實現商品列表頁

mixins和generic一起使用
GenericAPIView繼承APIView,封裝了很多方法,比APIView功能更強大
用的時候需要定義queryset和serializer_class
GenericAPIView里面默認為空
ListModelMixin里面list方法幫我們做好了分頁和序列化的工作,只要調用就好了

from rest_framework import mixins, generics
from rest_framework.response import Response
from rest_framework.views import APIView

from goods.models import Goods
from goods.serializers import GoodsSerializer


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)

如果不寫get方法的話,是沒法通過get請求訪問的
這樣看起來代碼比之前的簡潔一點了
我們還可以通過給繼承ListAPIView來讓代碼更加簡介
ListAPIView源代碼如下

class ListAPIView(mixins.ListModelMixin,
                  GenericAPIView):
    """
    Concrete view for listing a queryset.
    """
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

可以看到ListAPIView繼承了mixins.ListModelMixingenerics.GenericAPIView
而且幫我們實現了get方法,和我們自己寫的get方法一樣
這樣的話,我們的代碼就長這樣了

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

運行結果和之前的一樣,但是代碼只有兩行

添加分頁功能

官網示例:
http://www.django-rest-framework.org/api-guide/pagination/#setting-the-pagination-style

settings.py

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 1,
}

DEFAULT_PAGINATION_CLASS: 分頁所使用的類
PAGE_SIZE: 每頁顯示的數量
下面的圖片路徑也已經進行了補全,連域名都加上了

運行訪問時可能會有一個警告
UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: <class 'goods.models.Goods'> QuerySet.
是因為我們沒有對取出的數據進行排序

    queryset = Goods.objects.all().order_by('id')

自定義分頁功能

http://www.django-rest-framework.org/api-guide/pagination/#modifying-the-pagination-style
首先注釋掉settings.py中的分頁
goods/views.py

class GoodsPagination(PageNumberPagination):
    '''
    商品列表自定義分頁
    '''
    # 默認每頁顯示的個數
    page_size = 10
    # 可以動態改變每頁顯示的個數
    page_size_query_param = 'page_size'
    # 頁碼參數 http://127.0.0.1:8000/goods/?page=2&page_size=30
    page_query_param = 'page'
    # 每頁最多能顯示多少體條
    # 僅當 page_size_query_param 設置時有效
    max_page_size = 20


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

page_size_query_param: 默認每頁顯示的是10條數據,可以通過這個變量來改變每頁顯示的數量
http://127.0.0.1:8000/goods/?page=2&page_size=30
這個數量又受到max_page_size這個變量的控制
當我們想要每頁顯示30條數據的時候,明顯的>20,所以每頁只顯示20條數據

viewsets和router完成商品列表頁

主要用到viewsets中的GenericViewSet

class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    """
    The GenericViewSet class does not provide any actions by default,
    but does include the base set of generic view behavior, such as
    the `get_object` and `get_queryset` methods.
    """
    pass

ViewSetMixin中重寫了as_view方法,可以將action和函數進行綁定

class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    '''
    商品列表頁
    '''
    queryset = Goods.objects.all()
    serializer_class = GoodsSerializer
    pagination_class = GoodsPagination

urls.py

from goods.views import GoodsListViewSet
goods_list = GoodsListViewSet.as_view({
    'get': 'list',
})
    
path('goods/', goods_list, name='goods'),

通過viewset的as_view方法,將get請求和list方法進行綁定
但是這樣的話需要手動綁定比較麻煩,drf提供了一種更簡單的使用方法
http://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/#using-routers

from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'goods', GoodsListViewSet, base_name='goods')


re_path('^', include(router.urls)),

drf的APIView、GenericView、viewsets和router的簡單分析

這是GoodsListViewSet的繼承關系

GenericViewSet 是最高的一層

往下

GenericViewSet(viewsets) ----drf

  GenericAPIView ---drf

    APIView ---drf

      View     ----django

這些view功能的不同,主要的是有mixin的存在
mixins總共有五種:
  CreateModelMixin
  ListModelMixin
  UpdateModelMixin
  RetrieveModelMixin
  DestoryModelMixin

Router提供了自動綁定的功能

drf的request和response介紹

http://www.django-rest-framework.org/api-guide/requests/

http://www.django-rest-framework.org/api-guide/responses/

drf的過濾

在使用drf的過濾器之前,請先安裝django-filter

pip install django-filter

http://www.django-rest-framework.org/api-guide/filtering/#api-guide

django-filter官網

添加到INSTALLED_APPS里面

INSTALLED_APPS = [
     'django_filters',
]

在goods目錄下新建filters.py

import django_filters

from goods.models import Goods


class GoodsFilter(django_filters.rest_framework.FilterSet):
    '''
    商品過濾的類
    '''
    # 兩個參數,field_name是要過濾的字段,lookup是執行的行為,‘小與等於本店價格’
    price_min = django_filters.NumberFilter(field_name="shop_price", lookup_expr='gte')
    price_max = django_filters.NumberFilter(field_name="shop_price", lookup_expr='lte')

    class Meta:
        model = Goods
        fields = ['price_min', 'price_max']

goods/views.py

from django_filters.rest_framework import DjangoFilterBackend
from goods.filters import GoodsFilter


class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    '''
    商品列表頁
    '''
    queryset = Goods.objects.all().order_by('id')
    serializer_class = GoodsSerializer
    pagination_class = GoodsPagination
    filter_backends = (DjangoFilterBackend,)
    # 自定義過濾器
    filter_class = GoodsFilter

drf的搜索和排序

http://www.django-rest-framework.org/api-guide/filtering/#searchfilter

http://www.django-rest-framework.org/api-guide/filtering/#orderingfilter

這里的排序,搜索使用的都是rest_framework里面的包,而不是django_filters里面的包

from rest_framework.filters import SearchFilter, OrderingFilter

class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    '''
    商品列表頁, 分頁, 過濾, 排序
    '''
    queryset = Goods.objects.all().order_by('id')
    serializer_class = GoodsSerializer
    pagination_class = GoodsPagination
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    # 自定義過濾器
    filter_class = GoodsFilter
    # 搜索,默認模糊查詢
    search_fields = ('name', 'goods_brief')
    # 排序
    ordering_fields = ('shop_price', 'add_time')


短短幾行代碼,就完成了商品列表頁的分頁,過濾,排序功能


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com