使用Django rest framework 编写restful接口(根据之前电商项目为例)

使用 Django restframework框架 编写API接口

一、在搭建rest framework接口之前我们先了解一下什么是API接口:

API是应用程序编程接口,他是软件系统不同组成部分衔接的约定。可以理解为预先定义的函数。设计目的在于可以提供应用程序得以访问某些特定软件或硬件的能力,而又无需获知这些软硬件源码,也无需理解他们的内部工作机制细节。

API通常是以http的形式提供,它隐藏的含义就是,只要你符合我定义的标准,你就可以来使用我。

常用模型:

1、BOM - 浏览器对象模型

  • 一套操作浏览器功能的API

  • 通过BOM可以操作浏览器窗口,比如:弹出框、控制浏览器跳转、获取分辨率等

2、DOM - 文档对象模型

  • 一套操作页面元素的API

  • DOM可以把HTML看做是文档树,通过DOM提供的API可以对树上的节点进行操作

二、Django 使用restframework搭建restful接口

在工作当中,逐渐多的出现不使用页面展示数据,直接使用数据接口,这样的好处:

  • 首先可以实现动静分离。将数据库和查询和页面渲染彻底分开。
  • 网站可以支持脚本批量开发。

最原始的接口搭建是使用类视图搭建接口,后来发现接口规范很难约束。就有前辈写了接口框架,使用框架进行规范的接口开发。这个叫restful,django拥有restful的插件djangorestframework

Django rest-framework官方文档

Django Rest framework 的流程:

  • 建立 Models
  • 依靠 Serialiers 将数据库取出的数据 Parse 为 API 的数据(可用于返回给客户端,也可用于浏览器显示)
  • ViewSet 是一个 views 的集合,根据客户端的请求(GET、POST等),返回 Serialiers 处理的数据,权限 Premissions 也在这一步做处理
  • ViewSet 可在 Routers 进行注册,注册后会显示在 Api Root 页上
  • 在 urls 里注册 ViewSet 生成的 view,指定监听的 url

1、安装restful接口所需插件

pip install djangorestframework
pip install django-filter
pip install Markdown      

2、配置setting文件

①安装app:rest_framework
使用Django rest framework 编写restful接口(根据之前电商项目为例)_第1张图片

②安装rest_framework全局配置

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ]
}

3、创建serializers.py文件,用来存放接口的过滤器(文件位置无要求)

  序列化(Serializer)是 DRF 的核心概念,提供了数据的验证和渲染功能,其工作方式类似越 Django Form,当然也提供了对应 ModelForm 的 ModelSerializer。 和 Django Form 类似,Serializer 也是基于 Field 进行字段验证,Field 类都来自于rest_framework fields

  当前使用的HyperLinkedModelSerializer 是一个值得推荐的 Serializer,它能够自动为 HTMLRenderer 提供相关外键资源的超链接,便于 web 调试

详细序列化方法使用讲解

"""
    当前文件只是为了规定接口的模型及数据字段
"""
from rest_framework import serializers

from Store.models import *

# Serializers define the API representation.
class GoodsSerializer(serializers.HyperlinkedModelSerializer):
    """
    声明查询的表和要返回的字段
    """
    # 定义元类
    class Meta:
        # 要进行接口序列化的模型
        model = Goods
        # 序列化返回的字段
        fields = ['goods_name','goods_price','goods_number','goods_date','goods_safeDate','id']


# Serializers define the API representation.
class GoodsTypeSerializer(serializers.HyperlinkedModelSerializer):
    """
    声明查询的表和要返回的字段
    """
    # 定义元类
    class Meta:
        # 要进行接口序列化的模型
        model = GoodsType
        # 序列化返回的字段
        fields = ['name','description']

4、在视图当中查询接口要返回的数据,并指定序列化的类

  DRF 通过 View 提供 API 接口,一个 View 可以对应多个 Renderer,针对不同的渲染条件提供不同的输出格式(HTML/XML/JSON)。

  ViewSet 则是 View 的一个封装,一个 ViewSet 可以为同一个 URL 根据请求方法提供不同的接口。尤其是 ModelViewSet 会自动根据 Model 的定义生成 REST 接口和 URL,能够快速生成网站的一整套 API。

  定义一个 ViewSet 需要为其声明 queryset 和 serializer 属性

from rest_framework import viewsets

from Store.serializers import *
# v4.2 查询指定接口返回数据
class GoodsViewSet(viewsets.ModelViewSet):
    # 具体返回的数据
    queryset = Goods.objects.all()
    # 指定过滤的类
    serializer_class = GoodsSerializer

class GoodsTypeViewSet(viewsets.ModelViewSet):
    # 具体返回的数据
    queryset = GoodsType.objects.all()
    # 指定过滤的类
    serializer_class = GoodsTypeSerializer

5、在路由当中注册接口(主路由)

from django.urls import path,include,re_path

from Store.views import GoodsViewSet
from Store.views import GoodsTypeViewSet

from rest_framework import routers

# 声明一个默认的路由注册器
router = routers.DefaultRouter()
# 注册定义好的接口视图
router.register(r'goods',GoodsViewSet)
router.register(r'goodsType',GoodsTypeViewSet)

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^API',include(router.urls)), # restful的根路由
    re_path(r'^api-auth',include('rest_framework.urls')), # 接口认证路由
]

6、访问restful接口,查看API效果
使用Django rest framework 编写restful接口(根据之前电商项目为例)_第2张图片

使用Django rest framework 编写restful接口(根据之前电商项目为例)_第3张图片

使用Django rest framework 编写restful接口(根据之前电商项目为例)_第4张图片

7、使用python来请求接口数据

import requests

url = "http://127.0.0.1:8000/APIgoods/"

response = requests.get(url)

json_content = response.json()

for goods in json_content:
    print(goods)

使用Django rest framework 编写restful接口(根据之前电商项目为例)_第5张图片

三、通过vue请求接口方式,进行前端的数据渲染

在web端使用接口,通常要用到ajax和vue,数据通过接口返回
然后前端使用ajax进行请求,使用vue进行渲染

Vue.use(VueResource);
var vue = new Vue(
    {
        el: "#goods",
        data: {
            goods_list: []
        },
        created:function () {
            this.$http.get("/APIgoods/").then(
                function (data) {
                    this.goods_list = data.data;
                    console.log(data.data)
                },
                function (error) {
                    console.log(error)
                }
            )
        },
        methods: {

        }
    }
);

四、使用restful接口进行分页

在之前restful接口搭建完成的基础上,在setting rest_framework全局配置上再添加分页配置:
  DEFAULT_PAGINATION_CLASS :rest_framework自带的分页器
  PAGE_SIZE :单页的数量

# v4.1 restful接口配置
REST_FRAMEWORK = {
    # 权限
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ],
    # 分页
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE':5,#每页数据数量

}

上面是后端开发要完成的工作

前端开发需要基于当前的接口使用vue-resource进行接口请求和数据绑定
1、数据绑定首先要解决django和vue的代码冲突问题
因为vue使用{{}},而django也使用{{}},因此会冲突
使用Django标签{% verbatim myblock %} {% endverbatim myblock %}禁止使用Django标签渲染
被此标签包裹的代码将不会被Django的模板引擎渲染
在这里插入图片描述

2、解决vue绑定的范围问题
在之前绑定的标签基础上,在最外层加了一个div,将vue数据绑定范围扩大到这个div上
使用Django rest framework 编写restful接口(根据之前电商项目为例)_第6张图片
3、各种绑定的使用
{{ }}变量
在这里插入图片描述
v-for=”i in range(10)”
在这里插入图片描述
v-on:click=func(arg)
在这里插入图片描述
4、访问api接口,查看安装分页配置后API接口数据有何变化
可以发现在安装接口分页功能后,api文档有明显的变化,新增个4个参数:count,next,previous,results
使用Django rest framework 编写restful接口(根据之前电商项目为例)_第7张图片

5、根据分页的数据对前端页面进行相应vue配置

{% extends "store/base.html" %}

{% block title %}
    {{ store_name }}-商品列表页面
{% endblock %}

{% block label %}
    <a class="btn btn-warning" href="/Store/add_good/">添加商品a>
{% endblock %}

{% block content %}
    {% verbatim %}
    <div id="goods">
        <table class="table-bordered table">
            <thead>
                <tr align="center">
                    <th>商品名称th>
                    <th>商品价格th>
                    <th>商品数量th>
                    <th>出厂日期th>
                    <th>保质期th>
                    <th>操作th>
                tr>
            thead>

            <tbody >
                <tr v-for="goods in goods_list" align="center">
                    <td>
                        <a href="/Store/goods/{{ goods.id }}">{{ goods.goods_name }}a>
                    td>
                    <td>
                        <input type="text" v-bind:value="goods.goods_price" style="text-align: center">
                    td>
                    <td>{{ goods.goods_number }}td>
                    <td>{{ goods.goods_date }}td>
                    <td>{{ goods.goods_safeDate }}td>
                    <td>
                        <a class="btn btn-danger" href="/Store/set_goods/down/?id={{ goods.id }}">下架a>
                    td>
                tr>
            tbody>

        table>
        <div class="dataTables_paginate paging_simple_numbers">
            <ul class="pagination">
                <li class="paginate_button page-item " v-for="p in page_range">
                    <a class="page-link" href="#" v-on:click="get_page_data(p)">{{ p }}a>
                li>
            ul>
        div>

    div>
    {% endverbatim %}
{% endblock %}

{% block script %}
    <script src="/static/store/js/vue.min.js">script>
    <script src="/static/store/js/vue-resource.js">script>
    <script>
        Vue.use(VueResource);//声明是vueresource对象
        var vue = new Vue(
            {
                el:"#goods", //指定绑定的范围对象
                data:{
                    goods_list:[],
                    page_range:[]
                }, //具体绑定的数据对象
                created:function () { //发起ajax get请求
                    this.$http.get("/APIgoods/").then(
                        function (data) {
                            this.goods_list = data.data.results;//将接收的数据绑定到vue对象上
                            page_number = Math.ceil(data.data.count/5);//计算总页码向上取值,这里的5尽量与setting设置里的页码数据量一直例6.6取7
                            {#var page_range = [...new Array(page_number).keys()];//js生成页码列表#}
                            var page_range = Array.from({length:page_number},(item, index)=> index+1);
                            this.page_range = page_range;//将接收的数据绑定到vue对象上
                            {#console.log(page_range);#}
                            console.log(data.data);
                            console.log(page_range);
                            {#console.log(Array.from({length:10},(item, index)=> index+1))#}
                        },
                        function (error) {
                            console.log(error)
                        }
                    )
                },//初始化方法
                methods:{
                    get_page_data:function (page) {
                        this.$http.get("/APIgoods/?page="+page).then(
                        function (data) {
                            this.goods_list = data.data.results;//将接收的数据绑定到vue对象上
                            page_number = Math.ceil(data.data.count/5);//计算总页码向上取值,例6.6取7
                            var page_range = Array.from({length:page_number},(item, index)=> index+1);//js生成页码列表
                            this.page_range = page_range;//将接收的数据绑定到vue对象上
                            console.log(page_range);
                            console.log(data.data)
                        },
                        function (error) {
                            console.log(error)
                        }
                    )
                    }
                },//可以被v-on绑定的方法
            }
        );
    script>
{% endblock %}

现将vue代码摘出来,一一分析解释:
声明是vueresource对象
Vue.use(VueResource);

使用html中的Math.ceil方法进行向上取整,根据总数据量和每页数据量计算总页数
page_number = Math.ceil(data.data.count/5);

找个2中js方法,自动生成序列:
①[…new Array(10).keys()];这是方式只能从0开始0-9,其实也就是下标,但是页面不可能为0

②Array.from({length:10},(item, index)=> index+1);这种方式可以实现我们的需求,页面从1-10,它是根据下标+1的操作实现

点击页码进行跳转的方式同初始化一样,只不过在api访问方式上加上了页码参数,这是根据我们之前查询的restful接口的next,previous参数实现

<script src="/static/store/js/vue.min.js">script>
<script src="/static/store/js/vue-resource.js">script>
<script>
    Vue.use(VueResource);//声明是vueresource对象
    var vue = new Vue(
        {
            el:"#goods", //指定绑定的范围对象
            data:{
                goods_list:[],
                page_range:[]
            }, //具体绑定的数据对象,可以看做变量
            created:function () { //发起ajax get请求
                this.$http.get("/APIgoods/").then(
                    function (data) {
                        this.goods_list = data.data.results;//将接收的数据绑定到vue对象上
                        page_number = Math.ceil(data.data.count/5);//计算总页码向上取值,这里的5尽量与setting设置里的页码数据量一直例6.6取7
                        {#var page_range = [...new Array(page_number).keys()];//js生成页码列表#}
                        var page_range = Array.from({length:page_number},(item, index)=> index+1);
                        this.page_range = page_range;//将接收的数据绑定到vue对象上
                        {#console.log(page_range);#}
                        console.log(data.data);
                        console.log(page_range);
                        {#console.log(Array.from({length:10},(item, index)=> index+1))#}
                    },
                    function (error) {
                        console.log(error)
                    }
                )
            },//初始化方法
            methods:{
                get_page_data:function (page) {
                    this.$http.get("/APIgoods/?page="+page).then(
                    function (data) {
                        this.goods_list = data.data.results;//将接收的数据绑定到vue对象上
                        page_number = Math.ceil(data.data.count/5);//计算总页码向上取值,例6.6取7
                        var page_range = Array.from({length:page_number},(item, index)=> index+1);//js生成页码列表
                        this.page_range = page_range;//将接收的数据绑定到vue对象上
                        console.log(page_range);
                        console.log(data.data)
                    },
                    function (error) {
                        console.log(error)
                    }
                )
                }
            },//可以被v-on绑定的方法
        }
    );
script>

使用Django rest framework 编写restful接口(根据之前电商项目为例)_第8张图片

五、自定义restful接口返回内容

重写restframework框架renderer方法,自定义接口返回数据

1、在项目的根目录创建utils包用来存放要编写的renderer文件
使用Django rest framework 编写restful接口(根据之前电商项目为例)_第9张图片

2、在这个包下面创建一个py文件
使用Django rest framework 编写restful接口(根据之前电商项目为例)_第10张图片

3、重写renderer类

from rest_framework.renderers import JSONRenderer # 导入restful的renderer父类

class Customrenderer(JSONRenderer):
    def render(self, data, accepted_media_type=None, renderer_context=None):
        """
        :param data: 返回的数据
        :param accepted_media_type: 接收的类型
        :param renderer_context:  渲染呈现的内容
        """
        # 如果有请求数据过来:类似之前的if request.method == "POST"
        if renderer_context:
            # 判断返回的数据是否为字典
            if isinstance(data,dict):
                msg = data.pop("msg","请求成功") # 如果是字典,获取字典当中的msg键的值;若没有这个键,则给出一个回应
                code = data.pop("code",0) # 如果是字典,获取字典当中的code键的值;若没有这个键,则给出一个回应
            else:   # 非字典类型
                msg = "请求成功"
                code = 0
            # 重新构建返回数据的格式
            ret = {
                "msg":msg,
                "code":code,
                "author":"zhang",
                "data":data
            }
            # 根据父类方式返回数据格式
            return super().render(ret,accepted_media_type,renderer_context)
        else: # 如果没有发生修改则返回原格式数据
            return super().render(data,accepted_media_type,renderer_context)

4、setting中安装renderer
‘DEFAULT_RENDERER_CLASSES’:(
‘utils.rendererresponse.Customrenderer’,
)

# v4.1 restful接口配置
REST_FRAMEWORK = {
    # 权限
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ],

    # 自定义返回内容
    'DEFAULT_RENDERER_CLASSES': (
        'utils.rendererresponse.Customrenderer',
    ),
    
    # 分页
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE':5,

}

5、效果:
使用Django rest framework 编写restful接口(根据之前电商项目为例)_第11张图片

使用Django rest framework 编写restful接口(根据之前电商项目为例)_第12张图片
使用Django rest framework 编写restful接口(根据之前电商项目为例)_第13张图片

六、restful接口传参(过滤)-精确查询

接口传参

  • 接口传参:在安装模块的时候,安装过一个django-filter,这个模块是用来做数据过滤的模块。

1、必须安装django-filter模块

2、在settings当中安装django-filter插件
‘DEFAULT_FILTER_BACKENDS’: (
‘django_filters.rest_framework.DjangoFilterBackend’,# django-filters自带的查询过滤器
),

# v4.1 restful接口配置
REST_FRAMEWORK = {
    # 权限
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ],

    # 自定义返回内容
    'DEFAULT_RENDERER_CLASSES': (
        'utils.rendererresponse.Customrenderer',
    ),

    # 过滤器
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',# django-filters自带的查询过滤器
    ),

    # 分页
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE':5,

}

3、在视图里使用过滤器

from rest_framework import viewsets

from Store.serializers import *
# 导入过滤器
from django_filters.rest_framework import DjangoFilterBackend
# v4.2 查询指定接口返回数据
class GoodsViewSet(viewsets.ModelViewSet):
    # 具体返回的数据
    queryset = Goods.objects.all()
    # 指定过滤的类
    serializer_class = GoodsSerializer
    # v4.3 新增接口过滤
    filter_backends = [DjangoFilterBackend]  # 采用哪个过滤器
    filterset_fields = ['goods_name','goods_price']  # 进行查询的字段
    

4、效果
使用Django rest framework 编写restful接口(根据之前电商项目为例)_第14张图片

使用Django rest framework 编写restful接口(根据之前电商项目为例)_第15张图片

七、restful接口传参(过滤)-模糊查询

很多时候精确查询可能不适用大众的需求,用户很多时候是通过某个字段或词语进行模糊查找,就能匹配出很多结果;或者不是查询某个固定值的东西,就比如价格不可能就查询价格为100的商品,我们希望查询大于或小于某个价格的东西。

这个时候就需要我们自定义过滤器来实现模糊查询的需求

大致步骤和前面的精确查询差不多

1、首先安装django-filter模块

2、然后进行settings接口全局配置,安装App
使用Django rest framework 编写restful接口(根据之前电商项目为例)_第16张图片使用Django rest framework 编写restful接口(根据之前电商项目为例)_第17张图片
3、在项目中(尽量在项目主目录)创建一个filtes.py文件用于自定义过滤器

from django_filters import rest_framework as filters
from Store.models import *
#from rest_framework import generics

# 创建自定义过滤器类,继承django_filters.rest_framework.FilterSet类
class GoodsFilter(filters.FilterSet):
	# 设置goods_name商品名称查询过滤条件,icontains表示不区分大小写模糊查询
    goods_name = filters.CharFilter(field_name='goods_name', lookup_expr='icontains',label="模糊查询商品名")
    # 设置goods_price商品价格查询过滤条件,过滤最低价格所有商品信息
    min_price = filters.NumberFilter(field_name="goods_price", lookup_expr='gte',label="查询最低金额")
    # 设置goods_price商品价格查询过滤条件,过滤最高价格所有商品信息
    max_price = filters.NumberFilter(field_name="goods_price", lookup_expr='lte',label="查询最高金额")

    class Meta:
    	# 要对哪个模型数据进行接口过滤
        model = Goods
        # 过滤的字段,这个字段跟之前的精确查询不一样;是上面自定义的字段,是在接口url后的参数
        fields = ['goods_name', 'min_price','max_price']

4、视图中使用自定义过滤器

from rest_framework import viewsets

from Store.serializers import *
# 导入过滤器
from django_filters.rest_framework import DjangoFilterBackend
from DjangoShop.filters import GoodsFilter # 导入自定义过滤器
# v4.2 查询指定接口返回数据
class GoodsViewSet(viewsets.ModelViewSet):
    # 具体返回的数据
    queryset = Goods.objects.all()
    # 指定过滤的类
    serializer_class = GoodsSerializer
    # v4.3 新增接口过滤
    filter_backends = [DjangoFilterBackend]  # 采用哪个过滤器
    # filterset_fields = ['goods_name','goods_price']  # 进行查询的字段
    filter_class = GoodsFilter # 所使用的自定义过滤器类名

5、效果
使用商品名称进行模糊查询
使用Django rest framework 编写restful接口(根据之前电商项目为例)_第18张图片
对商品价格进行范围查询
使用Django rest framework 编写restful接口(根据之前电商项目为例)_第19张图片

你可能感兴趣的:(Django)