04-商品列表页
一、前后端分离优缺点
优点:
1、由于pc、app、pad多端适应 2、SPA开发模式开始流行 3、前后端开发职责不清 4、开发效率问题,前后端相互等待 5、前端一直配合后端,能力有限 6、后台开发语言和模板高度耦合
缺点:
1、前后端学习门槛增加 2、数据依赖,导致文档的重要性增加 3、前端工作量增加 4、SEO的难度加大(搜索引擎优化) 5、后端模式迁移成本增加
restful api
restful api是前后端分离目前来说是最佳实践,开发的标准规范,不是框架,是一种标准规范。优点就是轻量,直接通过http,不需要额外的协议,post/get/put/delete/patch/操作,同时还是面向资源,一目了然,具有解释性。第三就是数据描述简单,一般通过json或者xml做数据通信。每一个URI代表一种资源,客户端和服务器之间,传递这种资源的某种表现层,客户端通过五个HTTP动词,对服务器资源进行操作,实现表现层状态转化。
1、Django的View实现商品列表页
为了更好的理解restful api与Django原来的工作流程,我们在应用中新建一个views_base视图,然后写我们的逻辑。
Mxshop/urls.py
"""MxShop URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.2/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ # from django.contrib import admin from django.urls import path,re_path #将设置里的media路径导入进来进行配置,还有用到静态文件的serve方法 from MxShop.settings import MEDIA_ROOT from django.views.static import serve import xadmin from goods.views_base import GoodsListView urlpatterns = [ path('xadmin/', xadmin.site.urls), #用正则匹配media路径下的文件进行访问 re_path('media/(?P<path>.*)$',serve,{"document_root":MEDIA_ROOT}), path('goods/',GoodsListView.as_view(),name="goods_list") ]
goods/views_base.py:
__author__ = "lishuntao" __date__ = "2019/11/15 0015 12:26" import json from django.views.generic.base import View #CBV模式最常用也是最底层的View # from django.views.generic import ListView #和上面一样 from django.http import HttpResponse from goods.models import Goods class GoodsListView(View): def get(self,request): """ 通过Django的View实现商品列表页 :param request: :return:返回一个json格式的数据 """ json_list = [] goods = Goods.objects.all()[:10] 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) return HttpResponse(json.dumps(json_list),content_type="application/json")
然后启动项目,去浏览器中访问
数据自动转换为开发者模式,这样是不是方便多了啊。但这样编写视图,每次查询列表页,每次都要将模型类的字段赋值,这样是不是很不方便,甚至多了繁杂还容易出错,那有没有什么办法,我们开发者不用写这些繁琐的简单代码,让一种工具自动帮我们转换为dict格式,django.forms.models import model_to_dict就可以帮我们完成这个繁琐工作。
改动后的代码
import json from django.views.generic.base import View #CBV模式最常用也是最底层的View # from django.views.generic import ListView #和上面一样 from django.http import HttpResponse from goods.models import Goods class GoodsListView(View): def get(self,request): """ 通过Django的View实现商品列表页 :param request: :return:返回一个json格式的数据 """ json_list = [] goods = Goods.objects.all()[:10] # 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) #这下面的Django提供的方法model_to_dict 就是将模型类的字段,转换为dict形式 from django.forms.models import model_to_dict
for good in goods: json_dict = model_to_dict(good) json_list.append(json_dict) return HttpResponse(json.dumps(json_list),content_type="application/json")
启动项目,访问发现图片不能被序列化,除此之外,时间类型字段也不能被序列化。
有一个django提供的可以将这些不能被序列化的字段序列化,django.core import serializers:
__author__ = "lishuntao" __date__ = "2019/11/15 0015 12:26" import json from django.views.generic.base import View #CBV模式最常用也是最底层的View # from django.views.generic import ListView #和上面一样 from django.http import HttpResponse from goods.models import Goods class GoodsListView(View): def get(self,request): """ 通过Django的View实现商品列表页 :param request: :return:返回一个json格式的数据 """ json_list = [] goods = Goods.objects.all()[:10] # 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) #这下面的Django提供的方法model_to_dict 就是将模型类的字段,转换为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.core import serializers json_data = serializers.serialize("json",goods) json_data = json.loads(json_data) return HttpResponse(json_data,content_type="application/json")
可以发现,启动说的是在第一行解析错误,这是因为上面发送回客户端的数据必须,一定是json.dumps的数据,否则的话,就会解析错误。
__author__ = "lishuntao" __date__ = "2019/11/15 0015 12:26" import json from django.views.generic.base import View #CBV模式最常用也是最底层的View # from django.views.generic import ListView #和上面一样 from django.http import HttpResponse from goods.models import Goods class GoodsListView(View): def get(self,request): """ 通过Django的View实现商品列表页 :param request: :return:返回一个json格式的数据 """ json_list = [] goods = Goods.objects.all()[:10] # 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) #这下面的Django提供的方法model_to_dict 就是将模型类的字段,转换为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.core import serializers json_data = serializers.serialize("json",goods) json_data = json.loads(json_data)#序列化的数据一定是先反序列化后,发送回客户端在序列化发送 return HttpResponse(json.dumps(json_data),content_type="application/json")
当然,被json_data = serializers.serialize("json",goods)的返回值json_data数据直接就可以返回客户端,因为他已经自动帮我们把数据json.dumps啦,还有一种就是这里将不再演示。
刚才报错的图片和时间,都能够正常解析啦。(下面的是第二种django.http提供了json格式的返回,更加方便,只需要数据是一个字典格式即可)
2、APIView的方式实现商品列表页的功能(需要用到DRF所以去官方文档查看)
APIView使用前的配置,首先到项目路由urls.py下配置,否则以后引用APIView报错:
"""MxShop URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.2/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ # from django.contrib import admin import xadmin from django.urls import path,re_path #将设置里的media路径导入进来进行配置,还有用到静态文件的serve方法 from MxShop.settings import MEDIA_ROOT from django.views.static import serve from rest_framework.documentation import include_docs_urls from goods.views_base import GoodsListView urlpatterns = [ path('xadmin/', xadmin.site.urls), #用正则匹配media路径下的文件进行访问 re_path('media/(?P<path>.*)$',serve,{"document_root":MEDIA_ROOT}), path('goods/',GoodsListView.as_view(),name="goods_list"), path('docs/',include_docs_urls(title="天天生鲜")), ]
根据官方文档,让配置INSTALLED_APPS中注册rest_framework:
第二步官方文档要求注册这个路由,根据django版本配置urls.py:
"""MxShop URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.2/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ # from django.contrib import admin import xadmin from django.urls import path,re_path,include #将设置里的media路径导入进来进行配置,还有用到静态文件的serve方法 from MxShop.settings import MEDIA_ROOT from django.views.static import serve from rest_framework.documentation import include_docs_urls from goods.views_base import GoodsListView urlpatterns = [ path('xadmin/', xadmin.site.urls), path('api-auth/', include('rest_framework.urls')), #用正则匹配media路径下的文件进行访问 re_path('media/(?P<path>.*)$',serve,{"document_root":MEDIA_ROOT}), path('goods/',GoodsListView.as_view(),name="goods_list"), path('docs/',include_docs_urls(title="天天生鲜")), ]
注册好路由之后,这样就可以使用DRF的功能啦,首先要实现的是商品列表页功能,在Goods下的views.py中实现代码
from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from .models import Goods from .serializers import GoodsSerializer # Create your views here. class GoodsList(APIView): """ 商品列表页 """ def get(self,request,format=None): goods = Goods.objects.all()[:10] #自定义的序列化器(goods.serializers中) goods_serializer = GoodsSerializer(goods,many=True) return Response(goods_serializer.data)
视图中的GoodsSerializer类是自定义的序列化器,继承了DRF的serializers.Serializer。实现其中两个字段来理解他的功能,则serializers.py的代码为:
from rest_framework import serializers class GoodsSerializer(serializers.Serializer): name = serializers.CharField(default="",max_length=20) click_num = serializers.IntegerField(default=0,)
配置路由(urls.py):
from goods.views import GoodsList urlpatterns = [ path('xadmin/', xadmin.site.urls), path('api-auth/', include('rest_framework.urls')), #用正则匹配media路径下的文件进行访问 re_path('media/(?P<path>.*)$',serve,{"document_root":MEDIA_ROOT}), path('goods/',GoodsList.as_view(),name="goods_list"), path('docs/',include_docs_urls(title="天天生鲜")), ]
启动访问的结果:
继承Serializers.Serializer的类有create方法,重写这个方法,这是对模型类数据的创建:
def create(self, validated_data): """ 创建一个Goods对象, :param validated_data: :return: 这个将数据保存到数据库 """ return Goods.objects.create(**validated_data)
然后到视图函数中保存:
def post(self,request): """ 保存数据 :param request: :return: """ serializer = GoodsSerializer(data=request.data) if serializer.is_valid(): #在GoodsSerializer中调用模型类的create方法,然后在这里逻辑保存 serializer.save() return Response(serializer.data,status=status.HTTP_201_CREATED) return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST)
继承APIView的序列化器是不是和Django.forms很像,都是可以对数据进行验证,然后进行相关逻辑操作。forms.Model可以对数据模型类进行保存,序列化器也有modelserializer,将serializer的方法都实现了,这样用起来更简单,实现起来更方便。
一运行,浏览器访问,然后就能看见外键的详细数据,并且还能直接将原来复杂得直接简写,功能强大。
3、GenericView实现商品列表页和分页
第一去项目设置里面添加REST_FRAMEWORK设置:
REST_FRAMEWORK = { 'PAGE_SIZE': 10, }
自定义分类:
from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from rest_framework import mixins from rest_framework import generics from rest_framework.pagination import PageNumberPagination from .models import Goods from .serializers import GoodsSerializer # Create your views here. class GoodsSetPagination(PageNumberPagination): """ 定制分页设置 """ page_size = 1 page_size_query_param = 'page_size' page_query_param = "p" max_page_size = 10000 #GenericAPIView,对APIView进行一层封装,APIView是对View进行一层封装 class GoodsList(generics.ListAPIView): """ 商品列表页,继承第一个是为了列表页,还要必须继承一个视图类 ListAPIView已经将下面的代码完成 """ queryset = Goods.objects.all() serializer_class = GoodsSerializer pagination_class = GoodsSetPagination
4、viewsets和router完成商品列表页(所有的API基本上都会用Viewset完成)
views.py:
from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from rest_framework import mixins from rest_framework import generics from rest_framework.pagination import PageNumberPagination from rest_framework import viewsets from .models import Goods from .serializers import GoodsSerializer # Create your views here. class GoodsSetPagination(PageNumberPagination): """ 定制分页设置 """ page_size = 1 page_size_query_param = 'page_size' page_query_param = "p" max_page_size = 10000 #GenericAPIView,对APIView进行一层封装,APIView是对View进行一层封装 class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet): """ 商品列表页 """ queryset = Goods.objects.all() serializer_class = GoodsSerializer pagination_class = GoodsSetPagination
urls.py:
"""MxShop URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.2/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ # from django.contrib import admin import xadmin from django.urls import path,re_path,include #将设置里的media路径导入进来进行配置,还有用到静态文件的serve方法 from MxShop.settings import MEDIA_ROOT from django.views.static import serve from rest_framework.documentation import include_docs_urls from rest_framework.routers import DefaultRouter from goods.views_base import GoodsListView from goods.views import GoodsListViewSet router = DefaultRouter() #配置goods的url router.register(r"goods",GoodsListViewSet) # goods_list = GoodsListViewSet.as_view({ # "get":"list", # #DRF提供了router来配置这个路由 # }) urlpatterns = [ path('xadmin/', xadmin.site.urls), path('api-auth/', include('rest_framework.urls')), #用正则匹配media路径下的文件进行访问 re_path('media/(?P<path>.*)$',serve,{"document_root":MEDIA_ROOT}), # path('goods/',goods_list,name="goods_list"), re_path('^',include(router.urls)), path('docs/',include_docs_urls(title="天天生鲜")), ]
5、DRF的过滤
在应用下新建自定义的过滤,可以实现模糊查询等。
import django_filters from .models import Goods class GoodsFilter(django_filters.rest_framework.FilterSet): """ 商品过滤器 """ price_min = django_filters.NumberFilter(field_name="shop_price",lookup_expr="gte") price_max = django_filters.NumberFilter(field_name="shop_price", lookup_expr="lte") name = django_filters.CharFilter(field_name="name", lookup_expr="icontains") class Meta: model = Goods fields = ["price_min","price_max","name"]
在设置里面添加django_filters在Django中也可以自定义过滤
INSTALLED_APPS = [ 'django.contrib.auth', 'django.contrib.admin', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'users.apps.UsersConfig', 'goods.apps.GoodsConfig', 'trade.apps.TradeConfig', 'user_operation.apps.UserOperationConfig', 'DjangoUeditor', 'xadmin', 'crispy_forms',#这个是xadmin需要使用的 'rest_framework', 'django_filters', ]
在视图中的代码逻辑为
from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from rest_framework import mixins from rest_framework import generics from rest_framework.pagination import PageNumberPagination from rest_framework import viewsets from django_filters.rest_framework import DjangoFilterBackend from .models import Goods from .serializers import GoodsSerializer from .filters import GoodsFilter # Create your views here. class GoodsSetPagination(PageNumberPagination): """ 定制分页设置 """ page_size = 10 page_size_query_param = 'page_size' page_query_param = "p" max_page_size = 10000 #GenericAPIView,对APIView进行一层封装,APIView是对View进行一层封装 class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet): """ 商品列表页 """ queryset = Goods.objects.all() serializer_class = GoodsSerializer pagination_class = GoodsSetPagination filter_backends = [DjangoFilterBackend] #这是精确搜索过滤,我们需要的是模糊搜索 # filterset_fields = ['name', 'shop_price'] filter_class = GoodsFilter
运行结果是:
6、DRF的搜索和排序
views.py中的逻辑代码(搜索):
from rest_framework import filters class GoodsSetPagination(PageNumberPagination): """ 定制分页设置 """ page_size = 10 page_size_query_param = 'page_size' page_query_param = "p" max_page_size = 10000 #GenericAPIView,对APIView进行一层封装,APIView是对View进行一层封装 class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet): """ 商品列表页 """ queryset = Goods.objects.all() serializer_class = GoodsSerializer pagination_class = GoodsSetPagination filter_backends = [DjangoFilterBackend,filters.SearchFilter] #这是精确搜索过滤,我们需要的是模糊搜索 # filterset_fields = ['name', 'shop_price'] filter_class = GoodsFilter search_fields = ("name","goods_brief","goods_desc")
排序:
class GoodsSetPagination(PageNumberPagination): """ 定制分页设置 """ page_size = 10 page_size_query_param = 'page_size' page_query_param = "p" max_page_size = 10000 #GenericAPIView,对APIView进行一层封装,APIView是对View进行一层封装 class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet): """ 商品列表页 分页 搜索 过滤 排序 """ queryset = Goods.objects.all() serializer_class = GoodsSerializer pagination_class = GoodsSetPagination filter_backends = [DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter] #这是精确搜索过滤,我们需要的是模糊搜索 # filterset_fields = ['name', 'shop_price'] filter_class = GoodsFilter search_fields = ("name","goods_brief","goods_desc") ordering_fields = ("shop_price","sold_num","add_time")
总结DRF:首先我们通过创建views_base.py里面用Django的View来实现商品列表页,刚开始是自己序列化,然后通过介绍Django提供的方法model_to_dict,最后到使用Django原生的serializers以及使用它的JsonResponse的序列化,通过这些繁琐的工作,引出了DRF功能,首先通过APIView来完成,然后通过generics中的genericAPIView来实现了商品列表页,一直到最后使用ViewSets的东西来完成我们的商品列表页