商品列表頁
通過商品列表頁面來學(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ù)
好像還可以,這里繼續(xù)添加數(shù)據(jù)
json_dict["add_time"] = good.add_time
瀏覽器訪問
我們會發(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')
打開瀏覽器訪問
發(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)
看著效果挺不錯的,數(shù)據(jù)都加載進來了,但是缺點也挺明顯的
- 字段是寫死的,不靈活
- 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
的引入
瀏覽器訪問
這是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__'
外鍵被序列化為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__'
樂意看到,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.ListModelMixin
和generics.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)進行了補全,連域名都加上了
運行訪問時可能會有一個警告
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)系
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
添加到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
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')
短短幾行代碼,就完成了商品列表頁的分頁,過濾,排序功能