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

商品列表頁

通過商品列表頁面來學(xué)習(xí)drf

django的view實現(xiàn)商品列表頁


在goods目錄下新建一個views_base.py文件,用來區(qū)分drf的view和Dajngo自帶的view的區(qū)別
利用Django的view實現(xiàn)返回json數(shù)據(jù)

#!/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實現(xiàn)商品列表頁
        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數(shù)據(jù)


image

好像還可以,這里繼續(xù)添加數(shù)據(jù)

json_dict["add_time"] = good.add_time

瀏覽器訪問


image

我們會發(fā)現(xiàn)報錯了,這種方法是行不通的

django的serializer序列化model

model_to_dict

當(dāng)字段比較多時液走,一個字段一個字段的提取很麻煩,可以用model_to_dict,將model整個轉(zhuǎn)化為dict

class GoodsListView(View):
    def get(self, request):
        # 通過django的view實現(xiàn)商品列表頁
        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

發(fā)現(xiàn)依然報錯,ImageFieldFile 和add_time字段不能序列化
這種方法依然有局限性

django serializer
class GoodsListView(View):
    def get(self, request):
        # 通過django的view實現(xiàn)商品列表頁
        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

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

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

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

drf實現(xiàn)列表頁

安裝插件

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

配置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字段已經(jīng)幫我們補全了

drf的Modelserializer實現(xiàn)商品列表頁

上面是用Serializer實現(xiàn)的,需要自己手動添加字段遭商,如果用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實現(xiàn)商品列表頁
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實現(xiàn)商品列表頁
class GoodsSerializer(serializers.ModelSerializer):
    # 覆蓋外鍵字段
    category = CategorySerializer()

    class Meta:
        model = Goods
        fields = '__all__'
image

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

GenericView實現(xiàn)商品列表頁

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

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
而且?guī)臀覀儗崿F(xiàn)了get方法,和我們自己寫的get方法一樣
這樣的話,我們的代碼就長這樣了

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

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

添加分頁功能

官網(wǎng)示例:
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: 每頁顯示的數(shù)量
下面的圖片路徑也已經(jīng)進行了補全,連域名都加上了

image

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

    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):
    '''
    商品列表自定義分頁
    '''
    # 默認(rèn)每頁顯示的個數(shù)
    page_size = 10
    # 可以動態(tài)改變每頁顯示的個數(shù)
    page_size_query_param = 'page_size'
    # 頁碼參數(shù) http://127.0.0.1:8000/goods/?page=2&page_size=30
    page_query_param = 'page'
    # 每頁最多能顯示多少體條
    # 僅當(dāng) page_size_query_param 設(shè)置時有效
    max_page_size = 20


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

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

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和函數(shù)進行綁定

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的繼承關(guān)系

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官網(wǎng)

添加到INSTALLED_APPS里面

INSTALLED_APPS = [
     'django_filters',
]

在goods目錄下新建filters.py

import django_filters

from goods.models import Goods


class GoodsFilter(django_filters.rest_framework.FilterSet):
    '''
    商品過濾的類
    '''
    # 兩個參數(shù)座哩,field_name是要過濾的字段徒扶,lookup是執(zhí)行的行為,‘小與等于本店價格’
    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
    # 搜索,默認(rèn)模糊查詢
    search_fields = ('name', 'goods_brief')
    # 排序
    ordering_fields = ('shop_price', 'add_time')
image

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末根穷,一起剝皮案震驚了整個濱河市姜骡,隨后出現(xiàn)的幾起案子导坟,更是在濱河造成了極大的恐慌,老刑警劉巖圈澈,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惫周,死亡現(xiàn)場離奇詭異,居然都是意外死亡康栈,警方通過查閱死者的電腦和手機递递,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來啥么,“玉大人登舞,你說我怎么就攤上這事⌒伲” “怎么了菠秒?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長氯迂。 經(jīng)常有香客問我践叠,道長,這世上最難降的妖魔是什么嚼蚀? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任酵熙,我火速辦了婚禮,結(jié)果婚禮上驰坊,老公的妹妹穿的比我還像新娘。我一直安慰自己哮独,他們只是感情好拳芙,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著皮璧,像睡著了一般舟扎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上悴务,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機與錄音讯檐,去河邊找鬼羡疗。 笑死,一個胖子當(dāng)著我的面吹牛别洪,可吹牛的內(nèi)容都是我干的叨恨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼挖垛,長吁一口氣:“原來是場噩夢啊……” “哼痒钝!你這毒婦竟也來了秉颗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤送矩,失蹤者是張志新(化名)和其女友劉穎蚕甥,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體栋荸,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡菇怀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蒸其。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敏释。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖摸袁,靈堂內(nèi)的尸體忽然破棺而出钥顽,到底是詐尸還是另有隱情,我是刑警寧澤靠汁,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布蜂大,位于F島的核電站,受9級特大地震影響蝶怔,放射性物質(zhì)發(fā)生泄漏奶浦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一踢星、第九天 我趴在偏房一處隱蔽的房頂上張望澳叉。 院中可真熱鬧,春花似錦沐悦、人聲如沸成洗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓶殃。三九已至,卻和暖如春副签,著一層夾襖步出監(jiān)牢的瞬間遥椿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工淆储, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留冠场,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓本砰,卻偏偏與公主長得像慈鸠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容