Vue+Django2.0 REST framework 打造前后端分离的生鲜电商项目(五)商品列表页

一、Django2.0的view实现商品列表页

算是对以前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字符串。

小贴士:怎样理解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)统统转义为不可执行的字符串内容!

二、apiview方式实现商品列表页

1.drf(Django REST framework)所需插件:

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。(有的插件是安装上就可以了,有的不但需要安装,还需要配置,幸好这两个都属于前者。)

 2.实现商品列表页

1.配置 rest_framework

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 ]

2.drf写views

在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

三、drf实现商品列表页的功能

1.ModelSerializer(相当于django中的modelform,但是更加强大)

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 ,可以看到,商品列表中,商品的品牌表的数据也都展示了出来。

2.使用mixins让代码变得更简洁

1.精简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 #引入两个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

2.drf分页

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 即可看到分页效果

3.最重要的viewsets和router(路由器)来实现商品列表页

将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 即可看到商品列表页

4.drf的request和response

request.data返回请求主体的解析内容,这与django本身的request.POST+request.FILES属性类似。不同之处:

  • 它包括所有解析的内容,包括文件和非文件输入。
  • 它支持解析HTTP方法以外的内容POST,这意味着你可以访问内容PUTPATCH请求。
  • 它支持REST框架的灵活请求解析,而不仅仅是支持表单数据。例如,您可以像处理传入表单数据一样处理传入的JSON数据。

request.query_params相当于django本身的request.GET。任何HTTP方法类型都可能包含查询参数,而不仅仅是GET请求。

parsers解析器,通过对应方法,解析传过来的各种类型数据。

5.drf的过滤

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 即可看到商品列表页中,过滤之后的数据。

2.自定义过滤器

在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

效果图

6.drf的搜索和排序

1.搜索

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

效果图

2.排序

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏点滴积累

Fish Shell

今天看到阮一峰同学的一篇博客(Fish shell 入门教程),讲述的非常详细、清楚,有兴趣的可以直接转去查看此文,本文仅提供一下个人使用心得。 一、fish ...

38060
来自专栏北京马哥教育

MongoDB多纬度监控方法详解

一、mongostat工具方法 mongostat是mongdb自带的状态检测工具,在命令行下使用。它会间隔固定时间获取mongodb的当前运行状态,并输出。如...

48250
来自专栏Golang语言社区

Golang工程经验(上)

作为一个C/C++的开发者而言,开启Golang语言开发之路是很容易的,从语法、语义上的理解到工程开发,都能够快速熟悉起来;相比C、C++,Golang语言更简...

64620
来自专栏前端侠2.0

Angular技巧汇总 原

    声明项目的全局类型,同时不需要在各个Ts文件中import {XXX} from 'xxx'  ,就能直接引用!方法是:

12720
来自专栏熊二哥

快速入门系列--CLR--02多线程

最近,由于基础框架的整体升级,因此需要更新所有相关项目的DLL文件。这个过程存在不小的风险,因此也对发布后的生产服务器进行了密切的监控,结果还是出现了个别应用出...

19290
来自专栏Albert陈凯

2018-08-02 IntelliJ IDEA - Debug 调试多线程程序IntelliJ IDEA - Debug 调试多线程程序

https://blog.csdn.net/nextyu/article/details/79039566

22320
来自专栏Java Web

Java I/O不迷茫,一文为你导航!

学习过计算机相关课程的童鞋应该都知道,I/O 即输入Input/ 输出Output的缩写,最容易让人联想到的就是屏幕这样的输出设备以及键盘鼠标这一类的输入设备,...

12820
来自专栏大内老A

通过扩展改善ASP.NET MVC的验证机制[使用篇]

ASP.NET MVC提供一种基于元数据的验证方式是我们可以将相应的验证特性应用到作为Model实体的类型或者属性/字段上,但是这依然具有很多的不足。在这篇文章...

19950
来自专栏PHP实战技术

PHP ob_start() 函数介绍

php ob_start 与 ob_end_flush() 是 php 的缓冲输出函数。

41790
来自专栏竹清助手

NodeJS错误处理最佳实践

NodeJS的错误处理让人痛苦,在很长的一段时间里,大量的错误被放任不管。但是要想建立一个健壮的Node.js程序就必须正确的处理这些错误,而且这并不难学。如果...

27130

扫码关注云+社区

领取腾讯云代金券