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數據


image

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

json_dict["add_time"] = good.add_time

瀏覽器訪問


image

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

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

打開瀏覽器訪問


image

發現依然報錯,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)
image

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

  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的引入
瀏覽器訪問

image

這是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__'
image

外鍵被序列化為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__'
image

樂意看到,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: 每頁顯示的數量
下面的圖片路徑也已經進行了補全,連域名都加上了

image

運行訪問時可能會有一個警告
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的繼承關系

image

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
image

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

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

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容