算是对以前django知识的一个回顾,方便跟下面的drf(Django REST framework)实现商品列表页作对比
1.在apps/goods下新建views_base.py文件
1 import json
2 from django.views.generic.base import View
3
4 from .models import Goods
5
6
7 class GoodsListView(View):
8 def get(self,request):
9 """
10 通过django的view实现商品列表页
11 :param request:
12 :return:
13 """
14 json_list=[]
15 goods=Goods.objects.all()[:10]
16
17 # 最传统方案,麻烦且add_time序列化会报错
18 # for good in goods:
19 # json_dict={}
20 # json_dict["name"]=good.name
21 # json_dict["category"]=good.category.name
22 # json_dict["market_price"]=good.market_price
23 # json_list.append(json_dict)
24
25 # 改进方案,但是ImageFieldFile无法进行序列化,报错
26 # from django.forms.models import model_to_dict
27 # for good in goods:
28 # json_dict=model_to_dict(good)
29 # json_list.append(json_dict)
30
31 from django.core import serializers
32 json_data=serializers.serialize("json",goods)
33
34 # 前两个方案使用的代码
35 # from django.http import HttpResponse
36 # return HttpResponse(json.dumps(json_list),content_type="application/json")
37
38 from django.http import JsonResponse
39 return JsonResponse(json_data,safe=False)
2.在urls.py设置路由
1 from django.urls import path,include
2 import xadmin
3 #
4 from django.views.static import serve
5 from MXshop.settings import MEDIA_ROOT
6
7 from goods.views_base import GoodsListView
8
9 urlpatterns = [
10 path('xadmin/', xadmin.site.urls),
11 path('ueditor/',include('DjangoUeditor.urls')),
12 #配置上传文件的访问处理函数
13 path('media/<path:path>',serve,{'document_root':MEDIA_ROOT}),
14
15 path('goods', GoodsListView.as_view(),name="goods-list"),
16 ]
3.启动项目,访问:http://127.0.0.1:8000/goods 即可看到从后端传过来的,包含了goods数据信息的json字符串。
在views_base中,我被 JsonResponse,HttpResponse这两个模块之间的有什么不同所引起好奇心,都是返回字符串,一个可以返回json,而另一个需要添加一些设置才能返回json。那么何为json?我特意去查了一下,结果为:
1 JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。
2 它基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。
显然,这解释不像人话,于是我觉得自己动手丰衣足食才是硬道理。基于一条真理:
1 网络传输的数据都是字符串!
我将HTTPResponse中除了要返回的字符串,其他参数都删了,代替JsonResponse来作为return值。结果没有报错,于是我打开网页访问:http://127.0.0.1:8000/goods,发现虽然同样传输过来了字符串信息,但是字符串“并不纯”,有一些字符串居然发生了被执行了,换行什么的!于是,我明白了,json是什么?
1 json就是“纯”字符串!将字符串中一切可以被执行的内容(类似于\n)统统转义为不可执行的字符串内容!
1 coreapi(1.32.0+) - 模式生成支持。
2 Markdown(2.1.0+) - 可浏览API的Markdown支持。
3 django-filter(1.0.1+) - 过滤支持。
4 django-crispy-forms - 改进了用于过滤的HTML显示。
5 django-guardian(1.1.1+) - 对象级权限支持。
在pycharm==》files==》setting==》Project==》project interpreter看到已经安装好的插件
其中,已经有了:Markdown、django-filter、django-crispy-forms
安装 django-guardian、coreapi。(有的插件是安装上就可以了,有的不但需要安装,还需要配置,幸好这两个都属于前者。)
1.在urls.py中
1 from django.urls import path,include
2 import xadmin
3 #
4 from django.views.static import serve
5 from MXshop.settings import MEDIA_ROOT
6 from goods.views_base import GoodsListView
7 #
8 from rest_framework.documentation import include_docs_urls
9
10
11 urlpatterns = [
12 #
13 path('xadmin/', xadmin.site.urls),
14 path('ueditor/',include('DjangoUeditor.urls')),
15 #配置上传文件的访问处理函数
16 path('media/<path:path>',serve,{'document_root':MEDIA_ROOT}),
17 path('goods', GoodsListView.as_view(),name="goods-list"),
18 #
19 path('docs',include_docs_urls(title='慕学生鲜')),
20 path('api-auth/', include('rest_framework.urls',namespace='rest_framework')),
21 ]
2.在settings.py中注册rest_framework
1 INSTALLED_APPS = [
2 'django.contrib.admin',
3 'django.contrib.auth',
4 'django.contrib.contenttypes',
5 'django.contrib.sessions',
6 'django.contrib.messages',
7 'django.contrib.staticfiles',
8
9 'users.apps.UsersConfig',
10 'goods.apps.GoodsConfig',
11 'trade.apps.TradeConfig',
12 'user_operation.apps.UserOperationConfig',
13
14 'DjangoUeditor',
15 'crispy_forms',
16 'reversion',
17 'xadmin',
18 #
19 'rest_framework',
20 ]
在apps/goods下新建serializer.py文件(drf中的serializer.py等同于django中的forms.py)
1 from rest_framework import serializers
2
3
4 class GoodsSerializer(serializers.Serializer):
5 name=serializers.CharField(required=True,max_length=100)
6 click_num=serializers.IntegerField(default=0)
7 goods_front_image=serializers.ImageField()
在apps/goods/views中:
1 from .serializer import GoodsSerializer
2 from rest_framework.views import APIView
3 from rest_framework.response import Response
4
5 from .models import Goods
6 # Create your views here.
7
8 class GoodsListView(APIView):
9 """
10 List all snippets, or create a new snippet.
11 """
12 def get(self, request, format=None):
13 goods = Goods.objects.all()[:10]
14 #序列化
15 goods_serializer = GoodsSerializer(goods, many=True)
16 return Response(goods_serializer.data)
再配置一下urls.py
from django.urls import path,include
import xadmin
#
from django.views.static import serve
from MXshop.settings import MEDIA_ROOT
# from goods.views_base import GoodsListView
from rest_framework.documentation import include_docs_urls
from goods.views import GoodsListView
urlpatterns = [
path('xadmin/', xadmin.site.urls),
path('ueditor/',include('DjangoUeditor.urls')),
#配置上传文件的访问处理函数
path('media/<path:path>',serve,{'document_root':MEDIA_ROOT}),
path('goods', GoodsListView.as_view(),name="goods-list"),
path('docs',include_docs_urls(title='慕学生鲜')),
path('api-auth/', include('rest_framework.urls',namespace='rest_framework')),
]
浏览器访问:http://127.0.0.1:8000/goods 即可看到drf传过去的json数据信息了,而且做了一定程度的渲染。
小贴士:
在访问时,如果没有在xadmin后台退出账号,有可能会报错:
Django rest framework __str__ returned non-string (type NoneType)
是一个bug导致的这个报错:
首先,我们的UserProfile表继承的django/admin自动创建的用户表AbstractUser,
然后,我们在UserProfile表中用__str__返回的是name字段(昵称),而drf在找的是AbstractUser的username字段(用户名),没找到所以报错
解决方法:将UserProfile表中的__str__方法改成返回username:
1 def __str__(self):
2 return self.username
apps/goods/serializer.py
1 from rest_framework import serializers
2 from .models import Goods
3
4 class GoodsSerializer(serializers.ModelSerializer):
5 class Meta:
6 model=Goods
7 #序列化指定字段
8 #fields=('name','click_num','market_price','add_time')
9 #序列化全部字段
10 fields="__all__"
11
12
13
14
15 # class GoodsSerializer(serializers.Serializer):
16 # name=serializers.CharField(required=True,max_length=100)
17 # click_num=serializers.IntegerField(default=0)
18 # goods_front_image=serializers.ImageField()
19 #
20 # def create(self, validated_data):
21 # """
22 # Create and return a new `Snippet` instance, given the validated data.
23 # """
24 # return Goods.objects.create(**validated_data)
apps/goods/views.py同样可以使用
from .serializer import GoodsSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Goods
# Create your views here.
class GoodsListView(APIView):
"""
List all goods
"""
def get(self, request, format=None):
goods = Goods.objects.all()[:10]
goods_serializer = GoodsSerializer(goods, many=True)
return Response(goods_serializer.data)
#由于商品都是从后台添加的,不是通过用户从前端增加的,所以商品列表页用不到post
# def post(self, request, format=None):
# serializer = GoodsSerializer(data=request.data)
# if serializer.is_valid():
# serializer.save()
# return Response(serializer.data, status=status.HTTP_201_CREATED)
# return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
这时访问:http://127.0.0.1:8000/goods 是跟django中的modelform做到了同样的事儿,但modelserializer可以做到 序列化的嵌套
对apps/goods/serializer.py改写:
1 from rest_framework import serializers
2 from .models import Goods,GoodsCategory
3
4
5 class CategorySerializer(serializers.ModelSerializer):
6 class Meta:
7 model=GoodsCategory
8 fields="__all__"
9
10
11 class GoodsSerializer(serializers.ModelSerializer):
12 #实例化CategorySerializer
13 category=CategorySerializer()
14 class Meta:
15 model=Goods
16 fields="__all__"
这时访问:http://127.0.0.1:8000/goods ,可以看到,商品列表中,商品的品牌表的数据也都展示了出来。
1 from .serializer import GoodsSerializer
2 from rest_framework.views import APIView
3 from rest_framework.response import Response
4
5 #引入两个mixins所需的模块
6 from rest_framework import mixins
7 from rest_framework import generics
8
9 from .models import Goods
10 # Create your views here.
11
12
13 class GoodsListView(generics.ListAPIView):
14 """
15 List all goods
16 """
17 queryset = Goods.objects.all()[:10]
18 serializer_class = GoodsSerializer
drf是自带分页功能的,只要在views中进行一下规则的配置,就可以直接调用使用:
1 from .serializer import GoodsSerializer
2 from rest_framework.views import APIView
3 from rest_framework.response import Response
4
5 from rest_framework import mixins
6 from rest_framework import generics
7 from rest_framework.pagination import PageNumberPagination
8
9 from .models import Goods
10 # Create your views here.
11
12
13 class GoodsPagination(PageNumberPagination):
14 """
15 配置分页规则
16 """
17 page_size = 10
18 page_size_query_param = 'page_size'
19 page_query_param="p"
20 max_page_size = 1000
21
22 class GoodsListView(generics.ListAPIView):
23 """
24 List all goods
25 """
26 #queryset = Goods.objects.all() 用.all这种获取方法没什么大问题,只不过是没有代表
27 #将所有对象取出,而没有指定顺序,这就使得会出现:UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list
28 #的警告提示,使用下面的办法就会消除这种提示了。
29 queryset = Goods.objects.get_queryset().order_by('id')
30 serializer_class = GoodsSerializer
31 pagination_class = GoodsPagination
重启项目,访问:http://127.0.0.1:8000/goods 即可看到分页效果
将apps/goods/views.py改写为:
1 from .serializer import GoodsSerializer
2 from rest_framework.views import APIView
3 from rest_framework.response import Response
4
5 from rest_framework import mixins
6 from rest_framework import generics
7 from rest_framework.pagination import PageNumberPagination
8 from .models import Goods
9
10 from rest_framework import viewsets
11
12
13 # Create your views here.
14
15
16 class GoodsPagination(PageNumberPagination):
17 """
18 配置分页规则
19 """
20 page_size = 10
21 page_size_query_param = 'page_size'
22 page_query_param="p"
23 max_page_size = 1000
24
25
26 class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
27 """
28 商品列表
29 """
30 queryset = Goods.objects.get_queryset().order_by('id')
31 serializer_class = GoodsSerializer
32 pagination_class = GoodsPagination
将urls.py改写为:
from django.urls import path,include
import xadmin
#
from django.views.static import serve
from MXshop.settings import MEDIA_ROOT
# from goods.views_base import GoodsListView
from rest_framework.documentation import include_docs_urls
from rest_framework.routers import DefaultRouter
from goods.views import GoodsListViewSet
router = DefaultRouter()
router.register(r'goods', GoodsListViewSet)
urlpatterns = [
path('xadmin/', xadmin.site.urls),
path('ueditor/',include('DjangoUeditor.urls')),
#配置上传文件的访问处理函数
path('media/<path:path>',serve,{'document_root':MEDIA_ROOT}),
path('docs',include_docs_urls(title='慕学生鲜')),
path('api-auth/', include('rest_framework.urls',namespace='rest_framework')),
path('',include(router.urls))
]
重启项目,访问:http://127.0.0.1:8000/goods 即可看到商品列表页
request.data
返回请求主体的解析内容,这与django本身的request.POST
+request.FILES
属性类似。不同之处:
POST
,这意味着你可以访问内容PUT
和PATCH
请求。request.query_params
相当于django本身的request.GET。任何HTTP方法类型都可能包含查询参数,而不仅仅是GET请求。
parsers解析器,通过对应方法,解析传过来的各种类型数据。
1.简单的过滤,改写一下views:
1 from .serializer import GoodsSerializer
2 from rest_framework.views import APIView
3 from rest_framework.response import Response
4
5 from rest_framework import mixins
6 from rest_framework import generics
7 from rest_framework.pagination import PageNumberPagination
8 from .models import Goods
9
10 from rest_framework import viewsets
11
12
13 # Create your views here.
14
15
16 class GoodsPagination(PageNumberPagination):
17 """
18 配置分页规则
19 """
20 page_size = 10
21 page_size_query_param = 'page_size'
22 page_query_param="p"
23 max_page_size = 1000
24
25
26 class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
27 """
28 商品列表
29 """
30 # queryset = Goods.objects.get_queryset().order_by('id')
31 serializer_class = GoodsSerializer
32 pagination_class = GoodsPagination
33
34 def get_queryset(self):
35 return Goods.objects.filter(shop_price__gt=100)
重启项目,访问:http://127.0.0.1:8000/goods 即可看到商品列表页中,过滤之后的数据。
在settings.py中注册django_filter,在末尾配置过滤
1 INSTALLED_APPS = [
2 .....
3 'django_filters',
4
5 ]
6
7 ......
8
9 REST_FRAMEWORK = {
10 # 分页显示
11 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
12 'PAGE_SIZE': 10,
13 # 配置过滤
14 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
15 }
在apps/goods下新建filter.py 文件:
1 import django_filters
2 from .models import Goods
3
4 class GoodsFilter(django_filters.rest_framework.FilterSet):
5 """商品过滤器"""
6 price_min=django_filters.NumberFilter(name='shop_price',lookup_expr='gte')
7 price_max = django_filters.NumberFilter(name='shop_price', lookup_expr='lte')
8 #模糊查询,其中'contains'代表区分大小写,'icontains'代表不区分大小写
9 name = django_filters.CharFilter(name='name', lookup_expr='icontains')
10 class Meta:
11 model=Goods
12 fields=['price_min','price_max','name']
在apps/goods/views中引用改写:
1 ......
2
3 from rest_framework import viewsets
4 from django_filters.rest_framework import DjangoFilterBackend
5 from .filter import GoodsFilter
6 # Create your views here.
7
8 ......
9 class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
10 """
11 商品列表
12 """
13 queryset = Goods.objects.all()
14 serializer_class = GoodsSerializer
15 pagination_class = GoodsPagination
16 filter_backends = (DjangoFilterBackend,)
17 filter_class=GoodsFilter
效果图
apps/goods/views.py 内改写代码:
1 .......
2 from rest_framework import filters
3
4 ......
5
6 class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
7 """
8 商品列表
9 """
10 queryset = Goods.objects.all()
11 serializer_class = GoodsSerializer
12 pagination_class = GoodsPagination
13 filter_backends = (DjangoFilterBackend,filters.SearchFilter)
14 filter_class=GoodsFilter
15 search_fields = ('name', 'goods_brief','goods_desc')
效果图
apps/goods/views.py 内改写代码:
1 .........
2 from rest_framework import filters
3 ...........
4
5 ........
6
7 class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
8 """
9 商品列表
10 """
11 queryset = Goods.objects.all()
12 serializer_class = GoodsSerializer
13 pagination_class = GoodsPagination
14 filter_backends = (DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter)
15 filter_class=GoodsFilter
16 search_fields = ('name', 'goods_brief','goods_desc')
17 ordering_fields=('sold_num','add_time')
效果图
我们通过viewsets和一个类,完成了商品列表页,分页,过滤,搜索,排序。
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
"""
商品列表页,分页,过滤,搜索,排序
"""
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
pagination_class = GoodsPagination
filter_backends = (DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter)
filter_class=GoodsFilter
search_fields = ('name', 'goods_brief','goods_desc')
ordering_fields=('sold_num','add_time')