API是应用程序编程接口,他是软件系统不同组成部分衔接的约定。可以理解为预先定义的函数。设计目的在于可以提供应用程序得以访问某些特定软件或硬件的能力,而又无需获知这些软硬件源码,也无需理解他们的内部工作机制细节。
API通常是以http的形式提供,它隐藏的含义就是,只要你符合我定义的标准,你就可以来使用我。
常用模型:
1、BOM - 浏览器对象模型
一套操作浏览器功能的API
通过BOM可以操作浏览器窗口,比如:弹出框、控制浏览器跳转、获取分辨率等
2、DOM - 文档对象模型
一套操作页面元素的API
DOM可以把HTML看做是文档树,通过DOM提供的API可以对树上的节点进行操作
在工作当中,逐渐多的出现不使用页面展示数据,直接使用数据接口,这样的好处:
最原始的接口搭建是使用类视图搭建接口,后来发现接口规范很难约束。就有前辈写了接口框架,使用框架进行规范的接口开发。这个叫restful,django拥有restful的插件djangorestframework
Django rest-framework官方文档
Django Rest framework 的流程:
1、安装restful接口所需插件
pip install djangorestframework
pip install django-filter
pip install Markdown
2、配置setting文件
②安装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')), # 接口认证路由
]
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)
在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接口搭建完成的基础上,在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上
3、各种绑定的使用
{{ }}变量
v-for=”i in range(10)”
v-on:click=func(arg)
4、访问api接口,查看安装分页配置后API接口数据有何变化
可以发现在安装接口分页功能后,api文档有明显的变化,新增个4个参数:count,next,previous,results
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>
重写restframework框架renderer方法,自定义接口返回数据
1、在项目的根目录创建utils包用来存放要编写的renderer文件
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,
}
接口传参
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'] # 进行查询的字段
很多时候精确查询可能不适用大众的需求,用户很多时候是通过某个字段或词语进行模糊查找,就能匹配出很多结果;或者不是查询某个固定值的东西,就比如价格不可能就查询价格为100的商品,我们希望查询大于或小于某个价格的东西。
这个时候就需要我们自定义过滤器来实现模糊查询的需求
大致步骤和前面的精确查询差不多
1、首先安装django-filter模块
2、然后进行settings接口全局配置,安装App
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 # 所使用的自定义过滤器类名