04-商品列表页

一、前后端分离优缺点

优点:

  1、由于pc、app、pad多端适应  2SPA开发模式开始流行 3、前后端开发职责不清 4、开发效率问题,前后端相互等待 5、前端一直配合后端,能力有限 6、后台开发语言和模板高度耦合

缺点:

  1、前后端学习门槛增加 2、数据依赖,导致文档的重要性增加 3、前端工作量增加 4SEO的难度加大(搜索引擎优化) 5、后端模式迁移成本增加

restful api

  restful api是前后端分离目前来说是最佳实践,开发的标准规范,不是框架,是一种标准规范。优点就是轻量,直接通过http,不需要额外的协议,post/get/put/delete/patch/操作,同时还是面向资源,一目了然,具有解释性。第三就是数据描述简单,一般通过json或者xml做数据通信。每一个URI代表一种资源,客户端和服务器之间,传递这种资源的某种表现层,客户端通过五个HTTP动词,对服务器资源进行操作,实现表现层状态转化。

1、Django的View实现商品列表页

  为了更好的理解restful apiDjango原来的工作流程,我们在应用中新建一个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")
View Code

 

 可以发现,启动说的是在第一行解析错误,这是因为上面发送回客户端的数据必须,一定是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")
View Code

当然,被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="天天生鲜")),
]
APIView使用前配置

根据官方文档,让配置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="天天生鲜")),
]
View Code

注册好路由之后,这样就可以使用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类是自定义的序列化器,继承了DRFserializers.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的方法都实现了,这样用起来更简单,实现起来更方便。

ModelSerializer

一运行,浏览器访问,然后就能看见外键的详细数据,并且还能直接将原来复杂得直接简写,功能强大。

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="天天生鲜")),
]
urls.py

 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"]
goods/filters.py

在设置里面添加django_filtersDjango中也可以自定义过滤

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',
]
seetings.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 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
views.py

运行结果是:

 

 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")
View Code

 

 排序:

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的东西来完成我们的商品列表页

posted @ 2019-11-17 16:53  一知.半解  阅读(376)  评论(0编辑  收藏  举报