Django实践-10RESTful架构和DRF入门
参考
官网:https://www.djangoproject.com/
博客:https://www.liujiangblog.com/
本博客内容参考git:https://gitcode.net/mirrors/jackfrued/Python-100-Days 一些细节问题,大家可以查看git连接。本文主要的改变为把代码升级为django4.1版本。
Django静态文件问题备注:
参考:
Django测试开发-20-settings.py中templates配置,使得APP下的模板以及根目录下的模板均可生效
https://www.django-rest-framework.org/
RESTful架构
云计算服务
把软件(Software)、平台(Platform)、基础设施(Infrastructure)做成服务(Service)是很多IT企业都一直在做的事情,这就是大家经常听到的SasS(软件即服务)、PasS(平台即服务)和IasS(基础设置即服务)。
SOA服务
SOA包括了关于软件是如何被架构起来的东西,而SaaS是关于软件是如何被应用的。
在SaaS当中,应用程序可以像任何服务一样被传递,就像你家中电话的语音一样,看起来似乎就是为你的需求量体裁衣得到的。而SOA的定义和这个无丝毫的联系。SOA支持的服务,都是些离散的可以再使用的事务处理,这些事务处理合起来就组成了一个业务流程,是从基本的系统中提取出来的抽象代码。
SOA是一个框架的方法,而SaaS是一种传递模型。
通过SaaS传递Web服务并不需要SOA。
SaaS主要是指一个软件企业向其它企业提供软件服务。而SOA一般是企业内部搭建系统的基础。SaaS注重的是提供服务的思维。而SOA注重的是实现服务的思维。
实现面向服务的架构(SOA)有诸多的方式,包括RPC(远程过程调用)、Web Service、REST等,在技术层面上,SOA是一种抽象的、松散耦合的粗粒度软件架构;在业务层面上,SOA的核心概念是“重用”和“互操作”,它将系统资源整合成可操作的、标准的服务,使得这些资源能够被重新组合和应用。在实现SOA的诸多方案中,REST被认为是最适合互联网应用的架构,符合REST规范的架构也经常被称作RESTful架构。
REST与RESTful
RESTful的概念
要弄清楚什么是RESTful API,首先要弄清楚什么是REST。REST – REpresentational State Transfer,英语的直译就是“表现层状态转移”。解释一下什么是RESTful:
RESTful:URL定位资源,用HTTP动词(GET,POST,PUT,DELETE)描述操作。RESTful 是典型的基于HTTP的协议。
Resource:资源,即数据。
Representational:某种表现形式,比如用JSON,XML,JPEG等;
State Transfer:状态变化。通过HTTP动词实现。
RESTful架构是一种基于HTTP协议的Web应用程序设计风格,其特点包括使用HTTP动词(GET、POST、PUT、DELETE等)表示资源操作、使用URI标识资源、通过HTTP状态码表示操作结果等。RESTful架构具有简洁、可扩展、易于理解和实现、与不同编程语言和平台无关等优点,因此在Web服务、移动应用、IoT等领域得到广泛应用。
RESTful API 设计原则和规范
RESTful API 设计原则和规范
资源。资源就是网络上的一个实体,一段文本,一张图片或者一首歌曲。资源总是要通过一种载体来反应它的内容。JSON是现在最常用的资源表现形式。
统一接口。RESTful风格的数据元操CRUD(create,read,update,delete)分别对应HTTP方法:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源,这样就统一了数据操作的接口。
URI。可以用一个URI(统一资源定位符)指向资源,即每个URI都对应一个特定的资源。要获取这个资源访问它的URI就可以,因此URI就成了每一个资源的地址或识别符。一般的,每个资源至少有一个URI与之对应,最典型的URI就是URL。
无状态。所谓无状态即所有的资源都可以URI定位,而且这个定位与其他资源无关,也不会因为其他资源的变化而变化。
RESTful API设计准则
应该尽量将API部署在专用域名之下 [https://example.org/api/]
应该将API的版本号放入URL [https://example.org/app/1.0/foo], 但这个是不强制的
路径又被称为终点,表示API的具体地址,每个地址代表一种资源。资源只能是名词不能是动词,而且名词往往和数据库的表名相对应。同时,利用HTTP方法(post, get, put, delete)可以分离网址中资源名称的操作。
RESTful举例
GET:http://www.xxx.com/source/id 获取指定ID的某一类资源。例如GET:http://www.xxx.com/friends/123表示获取ID为123的会员的好友列表。如果不加id就表示获取所有会员的好友列表。
POST:http://www.xxx.com/friends/123表示为指定ID为123的会员新增好友。其他的操作类似就不举例了。
GET /products #返回所有的产品清单
POST /products #将产品新建到集合
GET /products/4 #将获取产品4
PATCH /products/4 #更新产品4(客户端提供改变后的完整资源)
PUT /products/4 #更新产品4(客户端提高改变的额属性)
DELETE /products/4 #删除产品4
HEAD #获取资源的元数据
OPTIONS #获取信息
请求方法(HTTP动词) | URI | 解释 |
GET | /students/ |
获取所有学生 |
POST | /students/ |
新建一个学生 |
GET | /students/ID/ |
获取指定ID的学生信息 |
PUT | /students/ID/ |
更新指定ID的学生信息(提供该学生的全部信息) |
PATCH | /students/ID/ |
更新指定ID的学生信息(提供该学生的部分信息) |
DELETE | /students/ID/ |
删除指定ID的学生信息 |
GET | /students/ID/friends/ |
列出指定ID的学生的所有朋友 |
DELETE | /students/ID/friends/ID/ |
删除指定ID的学生的指定ID的朋友 |
RDF实现
DRF使用入门
参考:https://www.django-rest-framework.org/
在Django项目中,如果要实现REST架构,即将网站的资源发布成REST风格的API接口,可以使用著名的三方库djangorestframework ,我们通常将其简称为DRF。
安装django-rest-framework
安装依赖
pip install djangorestframework==3.14.0 pip install markdown==3.4.3 pip install django-filter==23.1
配置settings.py
- 添加INSTALLED_APPS
INSTALLED_APPS = [ ... 'rest_framework', ]
- 添加urls.py
If you’re intending to use the browsable API you’ll probably also want to add REST framework’s login and logout views. Add the following to your root urls.py file.
urlpatterns = [ ... path('api-auth/', include('rest_framework.urls')), ]
Any global settings for a REST framework API are kept in a single configuration dictionary named REST_FRAMEWORK. Start off by adding the following to your settings.py module:
REST框架API的任何全局设置都保存在一个名为REST_FRAMEWORK的配置字典中。首先在settings.py模块中添加以下内容:
REST_FRAMEWORK = { # 配置默认页面大小 'PAGE_SIZE': 10, # 配置默认的分页类 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S', # 时间相关的字段 # 配置异常处理器 # 'EXCEPTION_HANDLER': 'api.exceptions.exception_handler', # 配置默认解析器 # 'DEFAULT_PARSER_CLASSES': ( # 'rest_framework.parsers.JSONParser', # 'rest_framework.parsers.FormParser', # 'rest_framework.parsers.MultiPartParser', # ), # 配置默认限流类 # 'DEFAULT_THROTTLE_CLASSES': (), # 配置默认授权类 # 'DEFAULT_PERMISSION_CLASSES': ( # 'rest_framework.permissions.IsAuthenticated', # ), # 配置默认认证类 # 'DEFAULT_AUTHENTICATION_CLASSES': ( # 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', # ), # api调试界面 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', # json渲染 'rest_framework.renderers.BrowsableAPIRenderer', # 浏览器渲染(生产环境可关掉) ), }
序列化
首先,我们将定义一些序列化器。让我们创建一个名为polls2/serializer.py的新模块,用于数据表示。
前后端分离的开发需要后端为前端、移动端提供API数据接口,而API接口通常情况下都是返回JSON格式的数据,这就需要对模型对象进行序列化处理。DRF中封装了Serializer类和ModelSerializer类用于实现序列化操作,通过继承Serializer类或ModelSerializer类,我们可以自定义序列化器,用于将对象处理成字典,代码如下所示。
from rest_framework import serializers from polls.models import Subject from django.contrib.auth.models import User, Group class SubjectSerializer(serializers.ModelSerializer): class Meta: model = Subject fields = '__all__' class UserSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = User fields = ['url', 'username', 'email', 'groups'] class GroupSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Group fields = ['url', 'name']
上面的代码直接继承了ModelSerializer,通过Meta类的model属性指定要序列化的模型以及fields属性指定需要序列化的模型字段,稍后我们就可以在视图函数中使用该类来实现对Subject模型的序列化。
在Group和User中,我们使用的是HyperlinkedModelSerializer的超链接关系。您也可以使用主键和各种其他关系,但是超链接是很好的RESTful设计。
编写视图函数
DRF框架支持两种实现数据接口的方式,一种是FBV(基于函数的视图),另一种是CBV(基于类的视图)。代码如下所示。
注释掉views.py中的原show_subjects函数,添加如下内容
from django.contrib.auth.models import User, Group from rest_framework import viewsets from rest_framework import permissions from serializer import SubjectSerializer, UserSerializer, GroupSerializer from django.http import HttpRequest, HttpResponse from rest_framework.decorators import api_view from rest_framework.response import Response # def show_subjects(request): # queryset = Subject.objects.all() # subjects = [] # for subject in queryset: # # subjects.append({ # # 'no': subject.no, # # 'name': subject.name, # # 'intro': subject.intro, # # 'isHot': subject.is_hot # # }) # subjects.append(SubjectMapper(subject).as_dict()) # return JsonResponse(subjects, safe=False,json_dumps_params={'ensure_ascii':False}) @api_view(('GET', )) def show_subjects(request: HttpRequest) -> HttpResponse: subjects = Subject.objects.all().order_by('no') # 创建序列化器对象并指定要序列化的模型 serializer = SubjectSerializer(subjects, many=True) # 通过序列化器的data属性获得模型对应的字典并通过创建Response对象返回JSON格式的数据 return Response(serializer.data) class UserViewSet(viewsets.ModelViewSet): """ API endpoint that allows users to be viewed or edited. """ queryset = User.objects.all().order_by('-date_joined') serializer_class = UserSerializer permission_classes = [permissions.IsAuthenticated] class GroupViewSet(viewsets.ModelViewSet): """ API endpoint that allows groups to be viewed or edited. """ queryset = Group.objects.all() serializer_class = GroupSerializer permission_classes = [permissions.IsAuthenticated]
对比上一个章节的使用bpmapper实现模型序列化的代码,使用DRF的代码更加简单明了,而且DRF本身自带了一套页面,可以方便我们查看我们使用DRF定制的数据接口,如下图所示。
修改urls.py
from django.urls import include, path from rest_framework import routers from polls2 import views as polls2_views router = routers.DefaultRouter() router.register(r'users', polls2_views.UserViewSet) router.register(r'groups', polls2_views.GroupViewSet) urlpatterns = [ # ... path('api-router/', include(router.urls)), path('api-auth/', include('rest_framework.urls', namespace='rest_framework')) ]
测试
http://127.0.0.1:8000/api/subjects
http://127.0.0.1:8000/api-auth/login http://127.0.0.1:8000/api-auth/logout
admin
admin
http://127.0.0.1:8000/api-router/
http://127.0.0.1:8000/api-router/users/
http://127.0.0.1:8000/api-router/groups/
基于RDF修改原有项目
编写序列化器serializer.py添加如下
from polls.models import Subject, Teacher class SubjectSimpleSerializer(serializers.ModelSerializer): class Meta: model = Subject fields = ('no', 'name') class TeacherSerializer(serializers.ModelSerializer): class Meta: model = Teacher exclude = ('subject', )
编写视图函数polls2/views.py中新增show_teachers
from polls.models import Subject, Teacher from serializer import SubjectSimpleSerializer, TeacherSerializer @api_view(('GET', )) def show_teachers(request: HttpRequest) -> HttpResponse: try: sno = int(request.GET.get('sno')) subject = Subject.objects.only('name').get(no=sno) teachers = Teacher.objects.filter(subject=subject).defer('subject').order_by('no') subject_seri = SubjectSimpleSerializer(subject) teacher_seri = TeacherSerializer(teachers, many=True) return Response({'subject': subject_seri.data, 'teachers': teacher_seri.data}) except (TypeError, ValueError, Subject.DoesNotExist): return Response(status=404)
配置URL映射。
urlpatterns = [ # ... path('api/teachers/', show_teachers), # 新增 ]
在static/html目录下新增api_teachers.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>老师信息</title> <style> #container { width: 80%; margin: 10px auto; } .teacher { width: 100%; margin: 0 auto; padding: 10px 0; border-bottom: 1px dashed gray; overflow: auto; } .teacher>div { float: left; } .photo { height: 140px; border-radius: 75px; overflow: hidden; margin-left: 20px; } .info { width: 75%; margin-left: 30px; } .info div { clear: both; margin: 5px 10px; } .info span { margin-right: 25px; } .info a { text-decoration: none; color: darkcyan; } </style> </head> <body> <div id="container"> <h1>[[ subject.name ]]学科的老师信息</h1> <hr> <h2 v-if="loaded && teachers.length == 0">暂无该学科老师信息</h2> <div class="teacher" v-for="teacher in teachers"> <div class="photo"> <img :src="'/static/images/' + teacher.photo" height="140" alt=""> </div> <div class="info"> <div> <span><strong>姓名:[[ teacher.name ]]</strong></span> <span>性别:[[ teacher.sex | maleOrFemale ]]</span> <span>出生日期:[[ teacher.birth ]]</span> </div> <div class="intro">[[ teacher.intro ]]</div> <div class="comment"> <a href="" @click.prevent="vote(teacher, true)">好评</a> (<strong>[[ teacher.good_count ]]</strong>) <a href="" @click.prevent="vote(teacher, false)">差评</a> (<strong>[[ teacher.bad_count ]]</strong>) </div> </div> </div> <a href="/static/html/api_subjects.html">返回首页</a> </div> <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js"></script> <script> let app = new Vue({ el: '#container', delimiters: ["[[", "]]"], // ***修改处*** vue 的与django模板语法冲突 data: { subject: {}, teachers: [], loaded: false }, created() { fetch('/api/teachers/' + location.search) .then(resp => resp.json()) .then(json => { this.subject = json.subject this.teachers = json.teachers }) }, filters: { maleOrFemale(sex) { return sex? '男': '女' } }, methods: { vote(teacher, flag) { let url = flag? '/praise/' : '/criticize/' url += '?tno=' + teacher.no fetch(url).then(resp => resp.json()).then(json => { if (json.code === 10000) { if (flag) { teacher.good_count = json.count } else { teacher.bad_count = json.count } } }) } } }) </script> </body> </html>
测试
http://127.0.0.1:8000/static/html/api_subjects.html
http://127.0.0.1:8000/static/html/api_teachers.html?sno=1
总结
本文主要是Django系列博客。本文是RESTful架构和DRF入门。
步骤如下:
1.安装依赖
2.在settings.py中INSTALLED_APPS注册rest_framework并配置 REST_FRAMEWORK 相关
3.在polls2的添加序列化模块serializer.py
4.在polls2的views.py中添加restful风格的函数
5.在urls.py中配置url映射
6.在static/html中新建前端页面
7.运行测试
如果你看到这里,且博客有帮助,可以关注,点赞,收藏,评论哈💯