美文网首页
Django+Vue打造购物网站(三)

Django+Vue打造购物网站(三)

作者: 听你讲故事啊 | 来源:发表于2018-12-01 11:40 被阅读0次

    商品列表页

    通过商品列表页面来学习drf

    django的view实现商品列表页


    在goods目录下新建一个views_base.py文件,用来区分drf的view和Dajngo自带的view的区别
    利用Django的view实现返回json数据

    #!/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实现商品列表页
            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数据


    image

    好像还可以,这里继续添加数据

    json_dict["add_time"] = good.add_time
    

    浏览器访问


    image

    我们会发现报错了,这种方法是行不通的

    django的serializer序列化model

    model_to_dict

    当字段比较多时,一个字段一个字段的提取很麻烦,可以用model_to_dict,将model整个转化为dict

    class GoodsListView(View):
        def get(self, request):
            # 通过django的view实现商品列表页
            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')
    

    打开浏览器访问


    image

    发现依然报错,ImageFieldFile 和add_time字段不能序列化
    这种方法依然有局限性

    django serializer
    class GoodsListView(View):
        def get(self, request):
            # 通过django的view实现商品列表页
            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)
    
    image

    看着效果挺不错的,数据都加载进来了,但是缺点也挺明显的

    1. 字段是写死的,不灵活
    2. image字段不完整

    这些缺点drf都可以帮我们来完成

    drf实现列表页

    安装插件

    pip install coreapi                         drf的文档支持
    pip install django-guardian           drf对象级别的权限支持
    
    APIview方式实现商品列表页

    配置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的引入
    浏览器访问

    image

    这是drf渲染的界面
    可以看到image字段已经帮我们补全了

    drf的Modelserializer实现商品列表页

    上面是用Serializer实现的,需要自己手动添加字段,如果用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实现商品列表页
    class GoodsSerializer(serializers.ModelSerializer):
        class Meta:
            model = Goods
            fields = '__all__'
    
    image

    外键被序列化为id,如果想要显示外键字段的信息,可以使用Serialzer的嵌套功能
    serializers.py

    class CategorySerializer(serializers.ModelSerializer):
        class Meta:
            model = GoodsCategory
            fields = "__all__"
    
    
    # ModelSerializer实现商品列表页
    class GoodsSerializer(serializers.ModelSerializer):
        # 覆盖外键字段
        category = CategorySerializer()
    
        class Meta:
            model = Goods
            fields = '__all__'
    
    image

    乐意看到,category字段显示的已经是详细信息了,不再是一个id了

    GenericView实现商品列表页

    mixins和generic一起使用
    GenericAPIView继承APIView,封装了很多方法,比APIView功能更强大
    用的时候需要定义queryset和serializer_class
    GenericAPIView里面默认为空
    ListModelMixin里面list方法帮我们做好了分页和序列化的工作,只要调用就好了

    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.ListModelMixingenerics.GenericAPIView
    而且帮我们实现了get方法,和我们自己写的get方法一样
    这样的话,我们的代码就长这样了

    class GoodsListView(generics.ListAPIView):
        '''
        商品列表页
        '''
        queryset = Goods.objects.all()
        serializer_class = GoodsSerializer
    

    运行结果和之前的一样,但是代码只有两行

    添加分页功能

    官网示例:
    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: 每页显示的数量
    下面的图片路径也已经进行了补全,连域名都加上了

    image

    运行访问时可能会有一个警告
    UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: <class 'goods.models.Goods'> QuerySet.
    是因为我们没有对取出的数据进行排序

        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):
        '''
        商品列表自定义分页
        '''
        # 默认每页显示的个数
        page_size = 10
        # 可以动态改变每页显示的个数
        page_size_query_param = 'page_size'
        # 页码参数 http://127.0.0.1:8000/goods/?page=2&page_size=30
        page_query_param = 'page'
        # 每页最多能显示多少体条
        # 仅当 page_size_query_param 设置时有效
        max_page_size = 20
    
    
    class GoodsListView(generics.ListAPIView):
        '''
        商品列表页
        '''
        queryset = Goods.objects.all()
        serializer_class = GoodsSerializer
        pagination_class = GoodsPagination
    

    page_size_query_param: 默认每页显示的是10条数据,可以通过这个变量来改变每页显示的数量
    http://127.0.0.1:8000/goods/?page=2&page_size=30
    这个数量又受到max_page_size这个变量的控制
    当我们想要每页显示30条数据的时候,明显的>20,所以每页只显示20条数据

    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和函数进行绑定

    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的继承关系

    image

    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

    django-filter官网

    添加到INSTALLED_APPS里面

    INSTALLED_APPS = [
         'django_filters',
    ]
    

    在goods目录下新建filters.py

    import django_filters
    
    from goods.models import Goods
    
    
    class GoodsFilter(django_filters.rest_framework.FilterSet):
        '''
        商品过滤的类
        '''
        # 两个参数,field_name是要过滤的字段,lookup是执行的行为,‘小与等于本店价格’
        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
    
    image

    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
        # 搜索,默认模糊查询
        search_fields = ('name', 'goods_brief')
        # 排序
        ordering_fields = ('shop_price', 'add_time')
    
    image

    短短几行代码,就完成了商品列表页的分页,过滤,排序功能

    相关文章

      网友评论

          本文标题:Django+Vue打造购物网站(三)

          本文链接:https://www.haomeiwen.com/subject/publcqtx.html