Django(9)|基于reseful-api风格的Django-framework
文章目录
- 一、restful-api接口
- 二、django-framework
- 1.安装并且简单使用django-framework
- 2.接口序列化
- 3.Django-framework的两种接口分页方式
- ①PageNumberPagination分页
- ②LimitOffsetPagination分页
- 4.其他常见的视图类
- ①准备工作
- ②原理解释
- 三、使用Django-framework写一个简单博客的接口
一、restful-api接口
1.定义: restful API 是一种符合rest风格的接口,rest是一种架构风格,采用http协议。
2.作用:
- 前后端分离一般会使用restful接口
- 可以使前后端解耦,减少服务器压力,前后端分工明确,提高安全性
3.主要原则:
- ①网络上的事务的都被抽象为资源
- ②每个资源都有唯一的标识符,并且对资源的各自操作不会改变标识符
- ③所有操作都是无状态的
- ④同一个资源具有多种表现形式(xml/json等),数据传输一般使用的格式是json(全是双引号),以前用的是webservice,数据传输格式是xml
4.restful API的一些建议
- ①建议使用https代替http,保证传输数据的安全
- ②url中要体现api标识:https://www.xx.com/api/xx/?id=1 或者https://api.xx.com/xx/?id=1
- ③url中要体现版本:https://www.xx.com/api/v1/xx/?id=1
- ④api接口一般使用名字不使用动词
- ⑤使用不同的方法来进行不同的操作(get/post/patch/put/delete).
- ⑥给用户返回一些状态码
- ⑦不同的请求方法返回不同类型的返回值,添加和更新一般返回添加的数据,删除不返回数据,获取数据一般返回列表套字典的形式
- ⑧操作异常返回错误信息
- ⑨推荐在返回数据的时候,返回其他相关联的一些数据的接口网址,例如分页器可以返回上一页下一页的网址
二、django-framework
1.安装并且简单使用django-framework
问题:为什么使用django-framework?
为了使前后端分离,我们需要始写数据接口,django-framework是基于restful风格的一种接口。
python安装三方包
pip install django-framework
接下来我们演示如何使用django-framework。
①创建项目并且注册应用
# 命令行执行以下命令
Django-admin startproject restful
python manage.py stratapp api
②创建一个模型类并且数据迁移
models.py中创建模型类
from django.db import modelsclass Category(models.Model):name=models.CharField(max_length=32,verbose_name='文章分类')class Article(models.Model):title=models.CharField(verbose_name='标题',max_length=32)summary=models.CharField(verbose_name='简介',max_length=32)content=models.TextField(verbose_name='内容')category=models.ForeignKey(verbose_name='分类',to='Category',on_delete=models.CASCADE)
命令行执行以下命令,进行数据迁移
python manage.py makemigrations
python manage.py migrate
③注册framework(django中的framework相当于一个应用)
settings.py
④使用指定方式进行增删改查
views.py
from django.shortcuts import render
from django.http import JsonResponse, HttpResponse
from api import models
from rest_framework.views import APIView
from rest_framework.response import Response
from django.forms.models import model_to_dictclass DrfCategoryView(APIView):def get(self,request,*args,**kwargs):# 拿所有数据/拿一条数据pk=kwargs.get('pk')if not pk:queryset=models.Category.objects.all().values('id','name')data=list(queryset)return Response(data)data=models.Category.objects.filter(id=pk).first()#查出来是对象print(data)if data:data=model_to_dict(data)return Response(data)def post(self, request, *args, **kwargs):'''增加一条分类信息'''# 有的数据会用name='ada'&age="15"拼接,那么request.post会获取不到这种类型的值, request.data会自动转换为字典格式的值# print(request.data)models.Category.objects.create(**request.data)#将信息打散再添加return Response('成功')def delete(self,request,*args,**kwargs):pk=kwargs.get('pk')models.Category.objects.filter(id=pk).first().delete()return Response('删除成功')def put(self, request, *args, **kwargs):pk=kwargs.get('pk')models.Category.objects.filter(id=pk).update(**request.data)return Response('更新成功')
url.py文件
urlpatterns = [path('admin/', admin.site.urls),re_path('^drf/category/$', views.DrfCategoryView.as_view()),re_path('^drf/category/(?P<pk>\d+)/$', views.DrfCategoryView.as_view()),
]
通过postman来查看
2.接口序列化
使用序列化可以在进行数据效验和序列化。
序列化后的增删改查views.py
from rest_framework import serializers # 序列化
from django.shortcuts import render
from django.http import JsonResponse, HttpResponse
from api import models
from rest_framework.views import APIView
from rest_framework.response import Response
from django.forms.models import model_to_dictclass NewCategorySerializer(serializers.ModelSerializer):class Meta:model = models.Category # 指定类# fields = "__all__" # 所有字段fields = ['id', 'name']# 展示的字段class NewCategoryView(APIView):def get(self, request, *args, **kwargs):# 拿所有数据/拿一条数据pk = kwargs.get('pk')if not pk:data = models.Category.objects.all() # 查出来是querysetser = NewCategorySerializer(instance=data, many=True)return Response(ser.data)data = models.Category.objects.filter(id=pk).first() # 查出来是对象ser = NewCategorySerializer(instance=data, many=False)print(ser.data)if data:data = model_to_dict(data)return Response(data)def post(self, request, *args, **kwargs):# 增加一条分类信息ser = NewCategorySerializer(data=request.data)if ser.is_valid():# 序列化ser.save()return Response(ser.data)return Response(ser.errors)def put(self, request, *args, **kwargs):pk = kwargs.get('pk')c_object = models.Category.objects.filter(id=pk).first()ser = NewCategorySerializer(instance=c_object, data=request.data)if ser.is_valid():ser.save()return Response(ser.data)return Response(ser.errors)def delete(self, request, *args, **kwargs):pk = kwargs.get('pk')models.Category.objects.filter(id=pk).first().delete()return Response('删除成功')
urls.py
re_path('^new/category/$', views.NewCategoryView.as_view()),
re_path('^new/category/(?P<pk>\d+)/$', views.NewCategoryView.as_view()),
3.Django-framework的两种接口分页方式
①PageNumberPagination分页
1.只返回数据
首先我们在urls.py中定义一个新的url
#分页
re_path('^page/category/$', views.PageView.as_view()),
re_path('^page/category/(?P<pk>\d+)/$', views.PageView.as_view()),
然后视图文件写分页类和接口类
from rest_framework.pagination import PageNumberPagination
from rest_framework import serializers# 分页类
class MyPageNumber(PageNumberPagination):# 每页最多两个数据,必须定义,因为底层page_size=None,如果不设置分页器name分页器不可用,# 我们也可以不在此处继承分页的类,那么根据源码需要在配置文件中设置page_sizepage_size = 2# 序列化类
class PageSer(serializers.ModelSerializer):class Meta:model = models.Categoryfields = "__all__"# 接口视图类
class PageView(APIView):def get(self, request, *args, **kwargs):queryset = models.Category.objects.all()# 方式一page_object = MyPageNumber()result = page_object.paginate_queryset(queryset, request, self)# queryset是传入所有对象去进行分页,request是拿page页数需要request,self是因为需要用当前对象的东西print(result, type(result))'''[<Category: Category object (3)>, <Category: Category object (4)>] <class 'list'>因为此处返回的是一个列表,里面是数据对象,因此我们需要序列化,所以去定义序列化类'''ser = PageSer(instance=result,many=True)return Response(ser.data)
查看结果,由于底层内部规定页数为page,因此在地址栏写入该网址查看信息http://127.0.0.1:8000/page/category/?page=3
注意:
①如果我们不想继承分页的类,但是源码处又必须设置每页的页码对象的数量
②那么我们还可以在配置文件中设置
settings.py
REST_FRAMEWORK = {"PAGE_SIZE": 2
}
③ 然后views.py 改为
# ---------------------------分页
from rest_framework.pagination import PageNumberPagination
from rest_framework import serializersclass PageSer(serializers.ModelSerializer):class Meta:model = models.Categoryfields = "__all__"class PageView(APIView):def get(self, request, *args, **kwargs):queryset = models.Category.objects.all()# 方式一page_object = PageNumberPagination()result = page_object.paginate_queryset(queryset, request, self)print(result, type(result))ser = PageSer(instance=result,many=True)return Response(ser.data)
后边我们都使用这种设置后的方式
2.数据+分页信息
views.py
rom rest_framework.pagination import PageNumberPagination
from rest_framework.generics import ListAPIView, GenericAPIView
from rest_framework import serializersclass PageSer(serializers.ModelSerializer):class Meta:model = models.Categoryfields = "__all__"class PageView(APIView):def get(self, request, *args, **kwargs):# 方式二:数据+分页信息'''queryset = models.Category.objects.all()page_object = PageNumberPagination()result = page_object.paginate_queryset(queryset, request, self)ser = PageSer(instance=result, many=True)return page_object.get_paginated_response(ser.data)'''# 方式三:数据+部分分页信息queryset = models.Category.objects.all() page_object = PageNumberPagination()result = page_object.paginate_queryset(queryset, request, self)ser = PageSer(instance=result, many=True)return Response({'count':page_object.page.paginator.count,'result':ser.data})
查看结果,在地址栏写入http://127.0.0.1:8000/page/category/?page=3
②LimitOffsetPagination分页
视图文件中写接口类和序列化的类,urls.py同上
from rest_framework.pagination import PageNumberPagination
from rest_framework.pagination import LimitOffsetPagination
from rest_framework import serializersclass PageSer(serializers.ModelSerializer):class Meta:model = models.Categoryfields = "__all__"class PageView(APIView):def get(self, request, *args, **kwargs):queryset = models.Category.objects.all()page_object = LimitOffsetPagination()result = page_object.paginate_queryset(queryset, request, self)ser = PageSer(instance=result, many=True)return Response(ser.data)
基本和上一个方法差不多,只不过继承的类不同,因此我们的网址变为http://127.0.0.1:8000/page/category/?offset=1&limit=2
跳过1条拿两条数据
但是如果我们limit数字设置很大,查看源码后我们可以设置最大的拿取数量,但是我们不建议直接修改源码,我们可以在views.py写个类
class NewLimitOffset(LimitOffsetPagination):max_limit = 2
修改后接口视图类别忘了使用新的类。
4.其他常见的视图类
①准备工作
准备工作,重新创建一个app。命令行输入
python manage.py startapp view
settings.py中注册应用
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','rest_framework','view.apps.ViewConfig',
]
在子应用中,创建自己的路由文件urls.py
from django.urls import path, re_path
from view import viewsurlpatterns = [re_path('^tag/$', views.TagView.as_view()),re_path('^tag/(?P<pk>\d+)/$', views.TagView.as_view()),
]
models.py创建模型
from django.db import modelsclass Tag(models.Model):title=models.CharField(max_length=32)
数据迁移
python manage.py makemigrations
python manage.py migrate
加一些数据
学习新的视图类
修改原视图
from .models import *
from rest_framework.serializers import ModelSerializer
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination# 导入新的视图类包
from rest_framework.generics import GenericAPIView, ListAPIView,CreateAPIView,UpdateAPIView,DestroyAPIViewclass TagSer(ModelSerializer):class Meta:model = Tagfields = "__all__"class TagView(ListAPIView,CreateAPIView,UpdateAPIView,DestroyAPIView):queryset = Tag.objects.all()serializer_class = TagSerpagination_class = PageNumberPagination
②原理解释
-
其实原理底层就是ListAPIView继承了GenericAPIView,GenericAPIView继承了APIView,APIView又继承了原始view
-
此视图已经实现我们原来视图中的增删改查四个功能
视图 解释 ListAPIView 源码实现了get方法 CreateAPIView 源码实现了post方法 UpdateAPIView 源码实现了put和patch方法 DestroyAPIView 源码实现了delete方法,注意是真删除,不是改变状态
例如我们看一下ListAPIVIEW中如何实现我们get方法
ctrl+l进入ListApiView源码
此处有我们的get方法,返回一个list方法,我们点进list方法
在list方法中,我们首先看到定义了查询的数据queryset,因此此处需要我们自己在类中写入我们的queryset
class TagView(ListAPIView):queryset = Tag.objects.all()#如果需要筛选的话,可以使用filter,例如id>3的#queryset = Tag.objects.filter(id__gt=3)
然后源码下边看到page,若我们分页的话则需要在类中继承分页的类,不写则全查询
class TagView(ListAPIView):queryset = Tag.objects.all()pagination_class = PageNumberPagination
再看源码,我们看到序列化,因此需要根据自己的业务逻辑写出序列化类,并且配置
class TagSer(ModelSerializer):class Meta:model = Tagfields = "__all__"class TagView(ListAPIView,CreateAPIView,UpdateAPIView,DestroyAPIView):queryset = Tag.objects.all()serializer_class = TagSerpagination_class = PageNumberPagination
最后源码返回了我们序列化后的数据,因此我们短短几行配置便实现了get方法
其他的post、put、patch、delete都和该方法类似
三、使用Django-framework写一个简单博客的接口
创建一个项目后,并创建一个app
Python manage.py startapp hg
settings配置文件中,注册应用,建议加apps.config
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','rest_framework','hg.apps.HgConfig',#可以自动加载类中的东西
]
models.py中创建模型
from django.db import models# 用户表
class UserInfo(models.Model):username = models.CharField(max_length=32, verbose_name='用户名')password = models.CharField(max_length=32, verbose_name='密码')# 文章表
class Article(models.Model):# 不会经常变化的值放在内存中:choices形式,避免跨表性能低category_choices = ((1, '咨询'),(2, '公司动态'),(3, '分享'),(4, '答疑'),(5, '其他'),)category = models.IntegerField(verbose_name='分类', choices=category_choices)title = models.CharField(verbose_name="标题", max_length=32)summary = models.CharField(verbose_name='简介', max_length=255)image = models.CharField(max_length=128, verbose_name='图片路径')author = models.ForeignKey(verbose_name='作者', to='UserInfo', on_delete=models.CASCADE)comment_count = models.IntegerField(verbose_name='评论数',default=0)read_count = models.IntegerField(verbose_name='阅读数',default=0)date = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')# 文章详情表
class ArticleDetail(models.Model):# 一般公司推荐分开,因为列太多,所以水平分表article = models.OneToOneField(verbose_name='文章表', to=Article, on_delete=models.CASCADE)content = models.TextField(verbose_name='内容')# 评论表
class Comment(models.Model):article = models.ForeignKey(verbose_name='文章', to='Article', on_delete=models.CASCADE)content = models.TextField(verbose_name='评论')user = models.ForeignKey(verbose_name='账户', to='UserInfo', on_delete=models.CASCADE)'''例如其他人在某个人的评论下引战,那么这个字段里写的就是某个人的id,如果就是自己单独评论文章那么就为nullparent=models.ForeignKey(verbose_name='回复',to='self',null=True,blank=True)'''
给用户加两条数据
在子应用中添加urls.py
写文章接口视图
urls.py
from django.urls import path,re_path,includefrom hg import views
urlpatterns = [re_path('^article/$', views.ArticleView.as_view()),re_path('^article/(?P<pk>\d+)/$', views.ArticleView.as_view()),
]
views.py
from django.shortcuts import render
from rest_framework import serializers
from hg import models
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination#此处序列化文章表,是为了post校验,但是略过作者,一般博客网站添加文章都是在后台添加
class ArticleSer(serializers.ModelSerializer):class Meta:model = models.Article# field = "__all__"exclude = ['author', ] # 不校验某个字段# 此处序列化文章表,是为了给我们的get请求
class ArticleListSer(serializers.ModelSerializer):class Meta:model = models.Articlefields = "__all__"# 此处序列化文章表,是为了给我们的get请求拿指定文章
class PageArticleSer(serializers.ModelSerializer):# 定义钩子,为了让get请求拿到的关联表数据的某些信息,而不是将关联表的信息全展示出content = serializers.CharField(source='articledetail.content')author = serializers.CharField(source='author.username')class Meta:model = models.Articlefields = "__all__"# 此处序列化文章内容表,是为了post请求校验
class ArticleDetailSer(serializers.ModelSerializer):class Meta:model = models.ArticleDetail# fields = "__all__"exclude = ['article', ] # 不校验某个字段# 文章的视图类
class ArticleView(APIView):def get(self, request, *args, **kwargs):# 获取文章列表pk = kwargs.get('pk')if not pk:quert_set = models.Article.objects.all().order_by('-date')pager = PageNumberPagination()result = pager.paginate_queryset(quert_set, request, self)ser = ArticleListSer(instance=result, many=True)return Response(ser.data)# 获取指定文章信息article_object = models.Article.objects.filter(id=pk).first()ser=PageArticleSer(instance=article_object,many=False)return Response(ser.data)def post(self, request, *args, **kwargs):# 添加文章ser = ArticleSer(data=request.data)ser_detail = ArticleDetailSer(data=request.data)if ser.is_valid() and ser_detail.is_valid():#同时校验# 增加文章article_object = ser.save(author_id=1) # save里可以增加校验时候略过的字段ser_detail.save(article=article_object) return Response("添加成功")return Response('添加失败!错误')
settings配置文件写接口分页
REST_FRAMEWORK = {"PAGE_SIZE": 2
}
演示:查询到的文章列表http://127.0.0.1:8000/hg/article/
演示:查询某个文章的信息http://127.0.0.1:8000/hg/article/2/