vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发

       这真是一个新起点, 看了这期的视频, 发现新学到好多内容, 老师也讲得好详细, 来看看我在这期学到的内容吧.

      本节参考博客:http://www.cnblogs.com/derek1184405959/p/8768059.html

一 基于django的view开发api

         这里我们暂且不用drf开发api, 我们用django的view开发api, 先看看django开发的view有什么bug, 然后再去学rest fream work, 这样才会发现drf的优点.

1. django view实现 goods列表页api

       开发一个基本的api应该是属于都django的基础知识吧. 我还是讲的详细一点吧, 加深自己的记忆.

       首先我们用cbv的方式开发一个api, 至于什么是cbv, 自己去百度一下吧.

       首先肯定是要写我们的view视图逻辑了, 我想直接在一个文件下开发, 这个文件也用于以后drf的开发, 为了区别开django自带的view, 我自己新建一个view_base.py文件(在goods下), 我们要新建一个view, 首先得继承django的最基础view, 在

django.views.generic 下, 所以应该这么写
from django.views.generic import View

然后因为我们要对提取goods的信息, 所以应该导入goods module的类.

from goods.models import Goods

然后再是写自己的view

class GoodsListView(View):

因为要获取goods列表, 所以应该用get方法, 而View类中有get方法, 所以我们只要重载get方法就行了,

在这我们就说说整个的逻辑是什么, 后面直接贴出代码来

1. 我们先定义一个列表, 用来存储goods的信息

2. 然后通过goods.object.all()方法获得全部的good信息(只是做示范, 提取10条数据)

3, 然后在遍历这些信息

4. 定义一个字典类型数据, 用于以后将字典转化为json类型返回

5. 往字典变量中增加一些字段的信息

6. 全部提取后再经过django的HttpResponse将转化为json的数据返回

 

代码如下:

from django.views.generic import View

from goods.models import Goods


class GoodsListView(View):
    """
    goodslistView视图类
    """
    def get(self, request):
        goods_list = []
        goods = Goods.objects.all()[:10]
        for good in goods:
            good_dict = {}
            good_dict["name"] = good.name
            good_dict["category"] = good.category.name
            good_dict["shop_price"] = good.shop_price
            goods_list.append(good_dict)

        from django.http import HttpResponse
        import json

        return HttpResponse(json.dumps(goods_list), content_type='application/json')

这里写完后我们要配置相应的路由信息, 在工程下的urls.py下写入以下代码:

from goods.view_base import GoodsListView

url('goods/', GoodsListView.as_view()),

然后我们运行工程, 访问http://127.0.0.1:8000/goods/  看以下返回的数据

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第1张图片

返回正确! 至于这样写有什么缺点呢? 

我们想想, 上面我们是不是没有写完全部字段, 如果要写全部字段, 是不是还要写很多逻辑, 这个django内部有相应的方法帮我们做好了, model_to_dict,将model整个转化为dict, 我们看看怎么用

from django.views.generic import View

from goods.models import Goods


class GoodsListView(View):
    """
    goodslistView视图类
    """
    def get(self, request):
        goods_list = []
        goods = Goods.objects.all()[:10]
        # for good in goods:
        #     good_dict = {}
        #     good_dict["name"] = good.name
        #     good_dict["category"] = good.category.name
        #     good_dict["shop_price"] = good.shop_price
        #     goods_list.append(good_dict)

        from django.forms.models import model_to_dict
        for good in goods:
            json_dict = model_to_dict(good)
            goods_list.append(json_dict)

        from django.http import HttpResponse
        import json

        return HttpResponse(json.dumps(goods_list), content_type='application/json')

运行项目, 访问网页端.报错:

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第2张图片

报错意思: ImageFieldFile字段不是json序列化, 说说出现的原理, 因为dumps做序列化时只能将一些数据做序列化, 有些字段是不能序列化的, 如何将所有字段做序列化? 我们yongdjango内部的方法--serializers 导入包

from django.core import serializers

代码:

# goods/view_base.py

from django.views.generic 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)

        import json
        from django.core import serializers
        from django.http import JsonResponse

        json_data = serializers.serialize('json',goods)
        json_data = json.loads(json_data)
        #In order to allow non-dict objects to be serialized set the safe parameter to False.
        return JsonResponse(json_data,safe=False)

再看看页面

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第3张图片

成功返回!!  是不是做到这用django做api开发就完美了呢? 答案是不是的, 不然人家drf干嘛还这么多人用呢?

缺点如下:

  • 字段序列化定死的,要想重组的话非常麻烦
  • 从上面截图可以看出来,images保存的是一个相对路径,我们还需要补全路径,而这些drf都可以帮助我们做到

以上写了这么多只是为了引入django rest framework和简单介绍django的序列化用法,下面就是重点讲解django rest framework了

二. 快速入门drf

1. 什么是序列化?

          今天总算学到想学的内容的, 老师终于带着我们开启了drf之旅, 在学习的过程中我想到一个问题, 就是什么是序列化?  因为贯穿整个视频的内容是序列化, 我以前没接触过, 也没有认真想过.

       在百度上找了一个解答就是: 通过将对象序列化可以将其存储在变量或者文件中,可以保存当时对象的状态,实现其生命周期的延长。并且需要时可以再次将这个对象读取出来, 简单的说就是, 为了方便对对象生命周期持久化.参考博客:

http://www.cnblogs.com/sun-haiyu/p/7087088.html

2. drf入门

     入门最好的方式就是参考官方文档进行入门. 地址: https://www.django-rest-framework.org/

    入门第一部肯定是配置好环境, 按照官方教程中查找需要安装什么包,

   vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第4张图片

      这些包自行安装.

     添加rest_freamework到INSTALL_APPS中

     vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第5张图片

    继续看下一步:

   vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第6张图片

    译文:

            如果您打算使用可浏览的API,您可能还需要添加REST框架的登录和注销视图。将以下内容添加到根urls.py文件中。

   这个的意思是, 添加这个url后, 我们通过浏览器访问api时, drf框架会给我们一个视图看, 方便查看, 这个我们肯定要的, 方便查看和调试,

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第7张图片

译文:

        让我们看一个使用REST框架构建一个简单的模型支持API的快速示例。我们将创建一个读写API,用于访问项目用户的信息。保留REST框架API的任何全局设置在名为REST_FRAMEWORK的单个配置字典中。首先将以下内容添加到settings.py模块: 我们照着做

这里开始就叫我们怎么使用了.

在工程下的urls.py下写入官方给的例子:

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第8张图片

运行后报错

这个错误是因为我们已经自定义的users, 所以要把导入的模块换一下, 换成如下代码:

"""VueShopProject URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.1/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.conf.urls import url,include
import xadmin
from django.views.static import serve
from VueShopProject.settings import MEDIA_ROOT
from goods.view_base import GoodsListView
import rest_framework

from users.models import UserProfile
from rest_framework import routers, serializers, viewsets


# Serializers define the API representation.
class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = UserProfile
        fields = ('url', 'username', 'email', 'is_staff')


# ViewSets define the view behavior.
class UserViewSet(viewsets.ModelViewSet):
    queryset = UserProfile.objects.all()
    serializer_class = UserSerializer


# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)

urlpatterns = [
    url('xadmin/', xadmin.site.urls),
    url(r'^ueditor/', include('DjangoUeditor.urls')),
    url(r'media/(?P.*)$', serve, {'document_root': MEDIA_ROOT}),
    url('goods/', GoodsListView.as_view()),

    url(r'^api-auth/', include('rest_framework.urls')),
    url(r'^', include(router.urls)),
]

运行后, 访问http://127.0.0.1:8000/  访问成功

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第9张图片

好了, 我们可是访问了, 接下来我们试着返回goods列表.

然后我们直接跳转到quickstart guide吧.

1. 第一步

      vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第10张图片

      第一步是相应的app下面建立一个序列化文件serializers.py, 我们在goods下建立该文件,下面那句话说:请注意,我们在这种情况下使用超链接关系,使用HyperlinkedModelSerializer。您还可以使用主键和其他各种关系,但超链接是一种很好的RESTful设计。我们先按照它的规范进行相应的更改, 更改为goods的

2. 第二步

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第11张图片

在app下面的view.py下写相应的代码, 我们在goods下的view.py下写:

from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer


class GoodsViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = Goods.objects.all().order_by('-date_joined')
    serializer_class = GoodsSerializer

3. 第三步

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第12张图片

最后一段译文:

       因为我们使用的是视图集而不是视图,所以我们可以通过简单地将视图集注册到路由器类来自动为我们的API生成URL conf。如果我们需要更多控制API URL,我们可以简单地使用常规基于类的视图,并明确地编写URL conf。最后,我们包括默认的登录和注销视图,以便与可浏览的API一起使用。这是可选的,但如果您的API需要身份验证并且您想要使用可浏览的API,则非常有用。

配置相应的urls :

"""VueShopProject URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.1/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.conf.urls import url,include
import xadmin
from django.views.static import serve
from VueShopProject.settings import MEDIA_ROOT
import rest_framework
from rest_framework import routers
from goods import views

router = routers.DefaultRouter()
router.register(r'goods', views.GoodsViewSet)


urlpatterns = [
    url('xadmin/', xadmin.site.urls),
    url(r'^ueditor/', include('DjangoUeditor.urls')),
    url(r'media/(?P.*)$', serve, {'document_root': MEDIA_ROOT}),

    url(r'^api-auth/', include('rest_framework.urls')),
    url(r'^', include(router.urls)),
]

4. 第四步

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第13张图片

译文:

        分页允许您控制每页返回的对象数。要启用它,请在tutorial / settings.py中添加以下行:

我们在这设置每页10个对象吧,

5. 第五步

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第14张图片

运行工程, 测试api,

报错:

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第15张图片

错误原因: 无法将关键字'date_joined'解析为字段 , 因为我们这里没有做更改

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第16张图片

module中根本没有这个字段, 我们改为添加时间吧,改为如下的views.py

from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer


class GoodsViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = Goods.objects.all().order_by('-add_time')
    serializer_class = GoodsSerializer

然后再次运行

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第17张图片

成功返回!  我们会不会想一个问题, 就是我用api方式获得数据的时候, 它不会是返回一个页面给我们吧, 我们用api测试工具试一下, 我用 http request maker做一下演示:

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第18张图片

返回:

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第19张图片

不会返回一个页面的, 那可能是drf内部的机理吧. 到此我们能成功返回我们想要的数据了, 这只是一个简单的列子, 我们再看看官方更详细的介绍, 看tutorial

地址: https://www.django-rest-framework.org/tutorial/1-serialization/

三. 仔细看文档

        我们使用的新版本的drf, 而视频中老师使用的老版本, 所以代码有些不一样, 新版本在老版本上做了一些封装, 有些功能实现起来更简单, 接下来我们看看细节方面的是, 继续看上面那个链接的文档: https://www.django-rest-framework.org/tutorial/1-serialization/

建工程, 建module等django的基础我们已经做了, 我们直接从建序列化文件开始

1. 尝试老师的写法

    在此之前我尝试了老师的写法, 无法调通, 可能两个版本有很大的不同了, 只能变的是把序列化文件变成

from .models import Goods
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()

而urls配置做相应的名称修改就行, 其他的不能修改.

2. 使用moduleSerializer

    这节课老师介绍了更优雅的写法, 把Serializer基于moduleSerializer写法. 这里说明了moduleSerializer类似与modulefrom用法, 我们按照给的例子写一下 

from .models import Goods
from rest_framework import serializers


class GoodsSerializer(serializers.ModelSerializer):
    """
    商品序列化
    """
    class Meta:
        model = Goods   # 要序列化的类
        fields = ('name', 'goods_sn', 'market_price', 'goods_desc') # 要序列化的字段

再运行一下, 发现是一样的效果

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第20张图片

         这里有一个问题, 当我列表的字段有外键时, 显示的是外键的id, 如下图所示:

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第21张图片

那我能不能该外键的所有信息呢?  那肯定可以的, 方法是重新定义一个序列化类, 然后再引用该外键的序列化的类中实例化该类就行了, 代码如下

from .models import Goods, GoodsCategory
from rest_framework import serializers


class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = GoodsCategory
        fields = "__all__"


class GoodsSerializer(serializers.ModelSerializer):
    """
    商品序列化
    """
    category = CategorySerializer() # 实例化对象的名字一定要和外键字段的名字一样 
    class Meta:
        model = Goods   # 要序列化的类
        fields = ('name', 'goods_sn', 'market_price', 'goods_desc', 'category') # 要序列化的字段

3. GenericView实现商品列表页

      有没有更优雅的方式实现列表页呢?  肯定有的, drf可是很强大的.那就是使用GenericView类, 我们翻到class based views 这一页.

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第22张图片

   然后翻到using mixins类,

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第23张图片

先看看官方介绍:

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第24张图片

译文:

使用基于类的视图的一大胜利是它允许我们轻松地编写可重用的行为。我们到目前为止使用的创建/检索/更新/删除操作对于任何模型都非常相似我们创建的支持API视图。这些常见行为在REST框架的mixin类中实现。让我们看一下如何使用mixin类组合视图。这是我们的views.py模块

我们照着例子写一下: view.py

from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer

from rest_framework import mixins
from rest_framework import generics


class GoodsViewSet(mixins.ListModelMixin, generics.GenericAPIView):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = Goods.objects.all().order_by('-add_time')
    serializer_class = GoodsSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

运行一下:

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第25张图片

一样的结果

上面的代码优化,可以直接继承ListAPIView,ListAPIView主要做了两件事:

  • ListAPIView(mixins.ListModelMixin,GenericAPIView)        继承了这两个类
  • 写好了get方法
  • vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第26张图片

       有没有发现ListAPIView和我们上面写的一模一样呢?  它是对 mixins.ListModelMixin,GenericAPIView这两个方法进行了封装

我们可以顺便看看其他的方法, 比如 CreateAPIView

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第27张图片

这里对post方法进行封装了, 以后我们要对数据表进行增删改查可以直接调用相应的xxxAPIView就行了. 要获得商品列表也的信息我们只需一下几行代码就行了

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

4.添加分页功能

分页功能其实我们一开始就做好了, 现在我们主要讲怎么改变url的一些参数, 我们在官方文档中看看怎么改变分页的一些参数吧

这个内容需要看api手册中的pagination页面

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第28张图片

我们看看这些参数在哪个api中设置, 看到这里就有答案了.

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第29张图片

这里说如果我们要定制自己的分页样式, 就需要覆盖以下的类, 我们按照官方的例子来改一下我们的view.py的代码, 在写的过程我发现一个问题, 就是 PageNumberPagination 这个类在哪呢?  找了一会就没看到, 我们往上找, 看到了

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 100
}

这里指出了类在 rest_framework.pagination下, view.py代码

from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer

from rest_framework import mixins
from rest_framework import generics

from rest_framework.pagination import PageNumberPagination


class StandardResultsSetPagination(PageNumberPagination):
    '''
        商品列表自定义分页
    '''
    # 默认每页显示的个数
    page_size = 10
    # 可以动态改变每页显示的个数
    page_size_query_param = 'page_size'
    # 页码参数
    page_query_param = 'p'
    # 最多能显示多少页
    max_page_size = 100


class GoodsViewSet(generics.ListAPIView):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = Goods.objects.all().order_by('-add_time')
    serializer_class = GoodsSerializer
    pagination_class = StandardResultsSetPagination

注意: 在url中加参数是是127.0.0.1:8000/goods/?xxx=xx & xxx =xx 形式, 一定要在前面加一个?号.

5.viewsets和router完成商品列表页

   听完这节视频后, 发现了更加优雅, 简便的方法实现商品列表, 那就是使用viewset, 我们还是看官方文档进行书写代码, 找到下面的目录

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第30张图片

打开 viewsets and routers,

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第31张图片

我们按照文档提示的例子, 把上节课写的代码改一下,

view.py

from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer

from rest_framework import mixins
from rest_framework import generics

from rest_framework.pagination import PageNumberPagination


class StandardResultsSetPagination(PageNumberPagination):
    '''
        商品列表自定义分页
    '''
    # 默认每页显示的个数
    page_size = 10
    # 可以动态改变每页显示的个数
    page_size_query_param = 'page_size'
    # 页码参数
    page_query_param = 'p'
    # 最多能显示多少页
    max_page_size = 100


class GoodsViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = Goods.objects.all().order_by('-add_time')
    serializer_class = GoodsSerializer
    pagination_class = StandardResultsSetPagination

urls.py

"""VueShopProject URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.1/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.conf.urls import url,include
import xadmin
from django.views.static import serve
from VueShopProject.settings import MEDIA_ROOT
import rest_framework
from rest_framework import routers
from goods import views

router = routers.DefaultRouter()
# router.register(r'goods', views.GoodsViewSet)

# 绑定get方法
goods_list = views.GoodsViewSet.as_view({
    'get': 'list',
})

urlpatterns = [
    url('xadmin/', xadmin.site.urls),
    url(r'^ueditor/', include('DjangoUeditor.urls')),
    url(r'media/(?P.*)$', serve, {'document_root': MEDIA_ROOT}),

    url(r'^api-auth/', include('rest_framework.urls')),
    url(r'^', include(router.urls)),
    url('goods/', goods_list, name="goods_list"),
]

运行后, 一样的效果.

6.  ViewSets和Routers结合使用

原来上面的代码还可以简化, 使用routers进行简化, 使用routers可以简化url的配置, 这个在大系统中更能体现出优势来.

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第32张图片

我们照着例子写一遍, urls.py

"""VueShopProject URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.1/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.conf.urls import url,include
import xadmin
from django.views.static import serve
from VueShopProject.settings import MEDIA_ROOT
import rest_framework
from rest_framework import routers
from goods import views

router = routers.DefaultRouter()
router.register(r'goods', views.GoodsViewSet)


urlpatterns = [
    url('xadmin/', xadmin.site.urls),
    url(r'^ueditor/', include('DjangoUeditor.urls')),
    url(r'media/(?P.*)$', serve, {'document_root': MEDIA_ROOT}),

    url(r'^api-auth/', include('rest_framework.urls')),
    url(r'^', include(router.urls)),
]

运行一下, 还是一样的返回值

8.drf的APIView、GenericView、viewsets和router的原理分析

genericViewSet 是最高的一层

往下

GenericViewSet(viewsets)     ----drf

  GenericAPIView                  ---drf

    APIView                        ---drf

      View            ----django

这些view功能的不同,主要的是有mixin的存在

 

mixins总共有五种:

  CreateModelMixin

  ListModelMixin

  UpdateModelMixin

  RetrieveModelMixin

  DestoryModelMixin

mixins.py源码

"""
Basic building blocks for generic class based views.

We don't bind behaviour to http method handlers yet,
which allows mixin classes to be composed in interesting ways.
"""
from __future__ import unicode_literals

from rest_framework import status
from rest_framework.response import Response
from rest_framework.settings import api_settings


class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}


class ListModelMixin(object):
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)


class RetrieveModelMixin(object):
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)


class UpdateModelMixin(object):
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)


class DestroyModelMixin(object):
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

mixins.py源码

 

以ListModelMixin为例:

如果不继承ListModelMixin的话,就无法将get和商品的列表关联起来,另外还有其中的分页等等,都无法实现。

还有其它几个mixin(增删改查局部),这些功能都是mixin做的

 

 我们一般都是用viewsets

ViewSet类与View类其实几乎是相同的,但提供的是read或update这些操作,而不是get或put 等HTTP动作。同时,ViewSet为我们提供了默认的URL结构, 使得我们能更专注于API本身。

 Router提供了一种简单,快速,集成的方式来定义一系列的urls

 

9.drf的request和response介绍

REST framework 的 Request 类扩展与标准的 HttpRequest,并做了相应的增强,比如更加灵活的请求解析(request parsing)和认证(request authentication)。

Request 解析

REST framwork 的 Request 对象提供了灵活的请求解析,允许你使用 JSON data 或 其他 media types 像通常处理表单数据一样处理请求。

.data

request.data 返回请求主题的解析内容。这跟标准的 request.POST 和 request.FILES 类似,并且还具有以下特点:

  • 包括所有解析的内容,文件(file) 和 非文件(non-file inputs)。
  • 支持解析 POST 以外的 HTTP method , 比如 PUT, PATCH
  • 更加灵活,不仅仅支持表单数据,传入同样的 JSON 数据一样可以正确解析,并且不用做额外的处理(意思是前端不管提交的是表单数据,还是 JSON 数据,.data 都能够正确解析)。

.data 具体操作,以后再说~

.query_params

request.query_params 等同于 request.GET,不过其名字更加容易理解。

为了代码更加清晰可读,推荐使用 request.query_params ,而不是 Django 中的 request.GET,这样那够让你的代码更加明显的体现出 ----- 任何 HTTP method 类型都可能包含查询参数(query parameters),而不仅仅只是 'GET' 请求。

.parser

APIView 类或者 @api_view 装饰器将根据视图上设置的 parser_classes 或 settings 文件中的 DEFAULT_PARSER_CLASSES 设置来确保此属性(.parsers)自动设置为 Parser 实例列表。

通常不需要关注该属性......

如果你非要看看它里面是什么,可以打印出来看看,大概长这样:

[, , ]

包含三个解析器 JSONParserFormParserMultiPartParser

注意: 如果客户端发送格式错误的内容,则访问 request.data 可能会引发 ParseError 。默认情况下, REST framework 的 APIView 类或者 @api_view 装饰器将捕获错误并返回 400 Bad Request 响应。 如果客户端发送的请求内容无法解析(不同于格式错误),则会引发 UnsupportedMediaType 异常,默认情况下会被捕获并返回 415 Unsupported Media Type 响应。

Responses

与基本的 HttpResponse 对象不同,TemplateResponse 对象保留了视图提供的用于计算响应的上下文的详细信息。直到需要时才会计算最终的响应输出,也就是在后面的响应过程中进行计算。 — Django 文档

REST framework 通过提供一个 Response 类来支持 HTTP 内容协商,该类允许你根据客户端请求返回不同的表现形式(如: JSON ,HTML 等)。

Response 类的子类是 Django 的 SimpleTemplateResponseResponse 对象使用数据进行初始化,数据应由 Python 对象(native Python primitives)组成。然后 REST framework 使用标准的 HTTP 内容协商来确定它应该如何渲染最终响应的内容。

当然,您也可以不使用 Response 类,直接返回常规 HttpResponse 或 StreamingHttpResponse 对象。 使用 Response 类只是提供了一个更好的交互方式,它可以返回多种格式。

除非由于某种原因需要大幅度定制 REST framework ,否则应该始终对返回 Response 对象的视图使用 APIView 类或 @api_view 装饰器。这样做可以确保视图执行内容协商,并在视图返回之前为响应选择适当的渲染器。

创建 response

Response()

与普通 HttpResponse 对象不同,您不会使用渲染的内容实例化 Response 对象。相反,您传递的是未渲染的数据,可能包含任何 Python 对象。

由于 Response 类使用的渲染器不能处理复杂的数据类型(比如 Django 的模型实例),所以需要在创建 Response 对象之前将数据序列化为基本的数据类型。

你可以使用 REST framework 的 Serializer 类来执行序列化的操作,也可以用自己的方式来序列化。

构造方法: Response(data, status=None, template_name=None, headers=None, content_type=None)

参数:

  • data: 响应的序列化数据。
  • status: 响应的状态代码。默认为200。
  • template_name: 选择 HTMLRenderer 时使用的模板名称。
  • headers: 设置 HTTP header,字典类型。
  • content_type: 响应的内容类型,通常渲染器会根据内容协商的结果自动设置,但有些时候需要手动指定。

属性

.data

还没有渲染,但已经序列化的响应数据。

.status_code

状态码

.content

将会返回的响应内容,必须先调用 .render() 方法,才能访问 .content 。

.template_name

只有在 response 的渲染器是 HTMLRenderer 或其他自定义模板渲染器时才需要提供。

.accepted_renderer

用于将会返回的响应内容的渲染器实例。

从视图返回响应之前由 APIView 或 @api_view 自动设置。

.accepted_media_type

内容协商阶段选择的媒体类型。

从视图返回响应之前由 APIView 或 @api_view 自动设置。

.renderer_context

将传递给渲染器的 .render() 方法的附加的上下文信息字典。

从视图返回响应之前由 APIView 或 @api_view 自动设置。

标准 HttpResponse 属性

Response 类扩展于 SimpleTemplateResponse,并且响应中也提供了所有常用的属性和方法。例如,您可以用标准方式在响应中设置 header:

response = Response()
response['Cache-Control'] = 'no-cache'

.render()

与其他任何 TemplateResponse 一样,调用此方法将响应的序列化数据呈现为最终响应内容。响应内容将设置为在 accepted_renderer 实例上调用 .render(data,accepted_media_type,renderer_context) 方法的结果。

通常不需要自己调用 .render() ,因为它是由 Django 处理的。

10.drf的过滤

       我们先看一下官方的文档怎么介绍drf的过滤的: https://www.django-rest-framework.org/api-guide/filtering/

       我们测试一下基于url参数的代码:

       vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第33张图片

       代码如下:  view.py

      

from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer

from rest_framework.pagination import PageNumberPagination
from rest_framework import generics


class StandardResultsSetPagination(PageNumberPagination):
    '''
        商品列表自定义分页
    '''
    # 默认每页显示的个数
    page_size = 10
    # 可以动态改变每页显示的个数
    page_size_query_param = 'page_size'
    # 页码参数
    page_query_param = 'p'
    # 最多能显示多少页
    max_page_size = 100


class GoodsViewSet(viewsets.ModelViewSet, generics.ListAPIView):
    """
    商品列表页
    """
    # queryset = Goods.objects.all().order_by('-add_time')    # 取出所有对象
    serializer_class = GoodsSerializer                      # 序列化相应的对象
    pagination_class = StandardResultsSetPagination         # 设置分页参数

    def get_queryset(self):
        """
        This view should return a list of all the purchases
        for the currently authenticated user.
        """
        queryset = Goods.objects.all()
        username = self.request.query_params.get('name', None)
        if username is not None:
            queryset = queryset.filter(name=username)
        return queryset

然后在url中输入: http://127.0.0.1:8000/goods/?name=融氏纯玉米胚芽油5l桶 返回一条记录

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第34张图片

我们再继续往下看,

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第35张图片

除了能够覆盖默认查询集之外,REST框架还包括对通用过滤后端的支​​持,允许您轻松构建复杂的搜索和过滤器

我们按着教程试一下这种方法 代码如下: view.py

from django_filters.rest_framework import DjangoFilterBackend

from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer

from rest_framework.pagination import PageNumberPagination
from rest_framework import generics


class StandardResultsSetPagination(PageNumberPagination):
    '''
        商品列表自定义分页
    '''
    # 默认每页显示的个数
    page_size = 10
    # 可以动态改变每页显示的个数
    page_size_query_param = 'page_size'
    # 页码参数
    page_query_param = 'p'
    # 最多能显示多少页
    max_page_size = 100


class GoodsViewSet(viewsets.ModelViewSet, generics.ListAPIView):
    """
    商品列表页
    """
    queryset = Goods.objects.all().order_by('-add_time')    # 取出所有对象
    serializer_class = GoodsSerializer                      # 序列化相应的对象
    pagination_class = StandardResultsSetPagination         # 设置分页参数

    filter_backends = (DjangoFilterBackend,)
    filter_fields = ('name', 'goods_desc')

然后运行, 报错:

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第36张图片

这个错误没发现在哪, 后来百度了一下, github有回复

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第37张图片

原来是版本的bug, 那么我们再往下看看, 

这里说了, 如果我们想更深入的了解django-filter, 得看相应的文档, 我们去看看文档吧

网址: https://django-filter.readthedocs.io/en/latest/index.html

我们看到关于drf的部分:https://django-filter.readthedocs.io/en/latest/guide/rest_framework.html

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第38张图片

我们在goods下新建一个filter文件, 用于存放过滤相应的代码, 我们按照这里的写法, 写一个关于我们module的方法

filter.py 代码:

import django_filters

from .models import Goods


class GoodsFilter(django_filters.rest_framework.FilterSet):
    """
    商品过滤类
    """
    price_min = django_filters.NumberFilter(name="shop_price", lookup_expr='gte')   # 最高价
    price_max = django_filters.NumberFilter(name="shop_price", lookup_expr='lte')   # 最低价

    class Meta:
        model = Goods   # 要过滤的类
        fields = ['price_min', 'price_max', 'category'] # 要过滤的字段

view.py代码:

from django_filters.rest_framework import DjangoFilterBackend

from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer
from .filter import GoodsFilter

from rest_framework.pagination import PageNumberPagination
from rest_framework import generics


class StandardResultsSetPagination(PageNumberPagination):
    '''
        商品列表自定义分页
    '''
    # 默认每页显示的个数
    page_size = 10
    # 可以动态改变每页显示的个数
    page_size_query_param = 'page_size'
    # 页码参数
    page_query_param = 'p'
    # 最多能显示多少页
    max_page_size = 100


class GoodsViewSet(viewsets.ModelViewSet):
    """
    商品列表页
    """
    queryset = Goods.objects.all().order_by('-add_time')    # 取出所有对象
    serializer_class = GoodsSerializer                      # 序列化相应的对象
    pagination_class = StandardResultsSetPagination         # 设置分页参数

    filter_backends = (DjangoFilterBackend,)                # 后台过滤设置
    filter_class = GoodsFilter                              # 要过滤的类和字段设置

注意: 别忘了在工程下的setting.py文件的 INSTALLED_APPS 加上 'django_filters'  如下图所示

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第39张图片

这样就可以运行了, 运行后访问: http://127.0.0.1:8000/goods/  然后右上角多了 如下标志:

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第40张图片

我们点击过滤器,设置如下价格做测试

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第41张图片

看看返回的数据:

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第42张图片

由原来的52 变为37个了, 我们也可以注意一点url:  /goods/?price_min=10&price_max=100&category=  我们同时可以在url下做过滤:

返回:

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第43张图片

好了,  过滤到此结束,

11.drf的搜索和排序

文档说明:

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第44张图片

我们先根据这里的提示, 添加我们的搜索功能, view.py 代码
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters

from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer
from .filter import GoodsFilter

from rest_framework.pagination import PageNumberPagination
from rest_framework import generics


class StandardResultsSetPagination(PageNumberPagination):
    '''
        商品列表自定义分页
    '''
    # 默认每页显示的个数
    page_size = 10
    # 可以动态改变每页显示的个数
    page_size_query_param = 'page_size'
    # 页码参数
    page_query_param = 'p'
    # 最多能显示多少页
    max_page_size = 100


class GoodsViewSet(viewsets.ModelViewSet):
    """
    商品列表页
    """
    queryset = Goods.objects.all().order_by('-add_time')    # 取出所有对象
    serializer_class = GoodsSerializer                      # 序列化相应的对象
    pagination_class = StandardResultsSetPagination         # 设置分页参数

    filter_backends = (DjangoFilterBackend, filters.SearchFilter)# 后台过滤设置
    filter_class = GoodsFilter                              # 要过滤的类和字段设置
    search_fields = ('name', 'goods_desc')                  # 搜索的字段

然后运行, 可以看看后台多了搜索框

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第45张图片

我们可以试着搜索:   精炼一级大豆油5L色拉油粮油食用油  结果是返回了一条数据, 我们再接下来看文档

这里可以通过正则表达式做匹配:

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第46张图片

自己试一下这些正则吧.

 添加排序功能

vue+django restful framework 电商项目(四) -- restful fream work 新起点, 实现商品列表页的api开发_第47张图片

根据教程, 我们加上排序功能吧, view.py代码

from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters

from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer
from .filter import GoodsFilter

from rest_framework.pagination import PageNumberPagination
from rest_framework import generics


class StandardResultsSetPagination(PageNumberPagination):
    '''
        商品列表自定义分页
    '''
    # 默认每页显示的个数
    page_size = 10
    # 可以动态改变每页显示的个数
    page_size_query_param = 'page_size'
    # 页码参数
    page_query_param = 'p'
    # 最多能显示多少页
    max_page_size = 100


class GoodsViewSet(viewsets.ModelViewSet):
    """
    商品列表页
    """
    queryset = Goods.objects.all().order_by('-add_time')    # 取出所有对象
    serializer_class = GoodsSerializer                      # 序列化相应的对象
    pagination_class = StandardResultsSetPagination         # 设置分页参数

    filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)# 后台过滤设置
    filter_class = GoodsFilter                              # 要过滤的类和字段设置
    search_fields = ('=name', 'goods_brief')                # 搜索的字段
    ordering_fields = ('sold_num', 'add_time')              # 排序功能

 

你可能感兴趣的:(Django,rest,framework)