赞
踩
1.定义: restful API 是一种符合rest风格的接口,rest是一种架构风格,采用http协议。
2.作用:
3.主要原则:
4.restful API的一些建议
问题:为什么使用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 models
class 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_dict class 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来查看
使用序列化可以在进行数据效验和序列化。
序列化后的增删改查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_dict class 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() # 查出来是queryset ser = 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()),
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_size page_size = 2 # 序列化类 class PageSer(serializers.ModelSerializer): class Meta: model = models.Category fields = "__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 serializers class PageSer(serializers.ModelSerializer): class Meta: model = models.Category fields = "__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 serializers class PageSer(serializers.ModelSerializer): class Meta: model = models.Category fields = "__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
视图文件中写接口类和序列化的类,urls.py同上
from rest_framework.pagination import PageNumberPagination from rest_framework.pagination import LimitOffsetPagination from rest_framework import serializers class PageSer(serializers.ModelSerializer): class Meta: model = models.Category fields = "__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
修改后接口视图类别忘了使用新的类。
准备工作,重新创建一个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 views
urlpatterns = [
re_path('^tag/$', views.TagView.as_view()),
re_path('^tag/(?P<pk>\d+)/$', views.TagView.as_view()),
]
models.py创建模型
from django.db import models
class 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,DestroyAPIView class TagSer(ModelSerializer): class Meta: model = Tag fields = "__all__" class TagView(ListAPIView,CreateAPIView,UpdateAPIView,DestroyAPIView): queryset = Tag.objects.all() serializer_class = TagSer pagination_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 = Tag
fields = "__all__"
class TagView(ListAPIView,CreateAPIView,UpdateAPIView,DestroyAPIView):
queryset = Tag.objects.all()
serializer_class = TagSer
pagination_class = PageNumberPagination
最后源码返回了我们序列化后的数据,因此我们短短几行配置便实现了get方法
其他的post、put、patch、delete都和该方法类似
创建一个项目后,并创建一个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,如果就是自己单独 评论文章那么就为null parent=models.ForeignKey(verbose_name='回复',to='self',null=True,blank=True) '''
给用户加两条数据
在子应用中添加urls.py
写文章接口视图
urls.py
from django.urls import path,re_path,include
from 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.Article fields = "__all__" # 此处序列化文章表,是为了给我们的get请求拿指定文章 class PageArticleSer(serializers.ModelSerializer): # 定义钩子,为了让get请求拿到的关联表数据的某些信息,而不是将关联表的信息全展示出 content = serializers.CharField(source='articledetail.content') author = serializers.CharField(source='author.username') class Meta: model = models.Article fields = "__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/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。