什么是drf ?
drf说白了就是django用来符合restful API接口规范的一个东西,说白了就是django的一个APP
下载安装drf
pip install djangorestframework
首先我们要明确,不基于DRF也是可以写接口的,DRF其实就是通过django的CBV模式,调用as_view方法和dispatch方法
DRF中有哪些内容
a.APIView
b.序列化组件
c.试图类(mixin)
d.认证组件
e.权限组件
f.频率组件
g.分页组件
h.解析器组件
i.相应其组件
j.url控制器
基于CBV写接口
views.py中
from django.shortcuts import render,HttpResponse,redirect import json from django.views import View from app01 import models class CourseView(View): def get(self,request): #拿到queryset类型的数据,要加工成[{},{}]这种数据类型 course_obj_list = models.Course.objects.all() ret = [] for course_obj in course_obj_list: ret.append({ "title":course_obj.title, "desc":course_obj.desc, }) return HttpResponse(json.dumps(ret,ensure_ascii=False)) #ensure_ascii=False是告诉json不要对中文进行编码,不然返回给前端的数据中如果有中文的话会被编码成unicode类型的数据,导致前端看不到中文
urls.py中
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ #url(r'^admin/', admin.site.urls), url(r'^courses/', views.CourseView.as_view(),name='courses'), #接口就写好啦 ]
基于APIView
views.py中
from django.shortcuts import render,HttpResponse,redirect import json from django.views import View from app01 import models from rest_framework.views import APIView #导入解析器 from rest_framework.parsers import JSONParser,FormParser,MulTiPartParser # JSONParser:解析json数据的额 # FormParser:解析urlencoded数据的 # FileUploadParser:解析文件数据的 class CourseView(APIView): #写一个类属性,名字必须是parser_classes parser_classes = [JSONParser,] #里面存放我们上面导入的那几个解析器,如果我们里面写了一个JSONParser,那么解析器只能解析前端发送过来的json数据,存放到request.data里面,可以通过postman测试一下看看效果,为什么?看源码吧 def get(self,request): course_obj_list = models.Course.objects.all() ret = [] for course_obj in course_obj_list: ret.append({ "title":course_obj.title, "desc":course_obj.desc, }) return HttpResponse(json.dumps(ret, ensure_ascii=False)) def post(self,request): print('post请求成功啦') #你会发现,即便是发送的数据类型不对,post方法也走了,但是request.data没有东西,那么肯定是它出了问题 print(request.data) #request.data对我们的数据进行解析的,那么说明data不是一个变量,而是一个属性方法,还记得属性方法吗 return HttpResponse('POST')
序列化组件
说白了就是写一个 serializer 的类,基本上写法和你的model中写的差不多,但是这个类中写的是你想序列化的字段,没写的字段不会有返回数据。
# 实例化的这个Courseserializer就是序列化器
class CourseSerializer(serializers.Serializer): title = serializers.CharField(max_length=32) desc = serializers.CharField(max_length=100) class Index(APIView): def get(self,request): obj = models.Course.objects.all() CS = CourseSerializer(obj,many=True)
# 通过返回对象的data属性就能拿到相应的数据 print(CS.data) # return render(request,'index.html',{'res_list':res_list}) return Response(CS.data) #列表中嵌套字段 def post(self,request): cs = CourseSerializer(data=request.data,many=False)
# 做一个简单的校验,也是类似form的东西 if cs.is_valid(): models.Course.objects.create(**cs.data) print('保存成功啦') return Response(cs.data)
# 如果没有发送成功,错误信息也是存放在这个对象中的 return Response(cs.errors)
为什么要中这个序列化组件呢?
因为django自带的序列化组件不能序列化JSON的数据,而这个序列化组件可以序列化所有类型的数据
如何对JSON序列化时间日期格式
import json from datetime import datetime from datetime import date #对含有日期格式数据的json数据进行转换 class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field,datetime): return field.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field,date): return field.strftime('%Y-%m-%d') else: return json.JSONEncoder.default(self,field) d1 = datetime.now() dd = json.dumps(d1,cls=JsonCustomEncoder)
返回指定状态码
#from rest_framework import status #返回指定状态码的时候会用到 #return Response(se_data,status=status=HTTP_400_BAD_REQUEST) #或者这种方式返回来指定状态码:return JsonResponse(serializer.data, status=201)
如果表中有一对多或者多对多关系该怎么处理呢?
class BookSerializer(serializers.Serializer): title = serializers.CharField(max_length=32) #针对一对多 publish_name = serializers.CharField(source='publish.name') #针对多对多 authors = serializers.SerializerMethodField()
#注意:这里的get_authors必须和你多对多字段名字相同(标红字段必须相同),obj对象就是你当前这个类的对象,当前就是BookSerializer对象 def get_authors(self,obj): li=[] author_list = obj.authors.all() for author in author_list: d={} d['name'] = author.name li.append(d) return li
对于携带参数的增删改查:(post, delete ,put ,get)
一种方法是我们在配置路由的时候,配置两个路由,比如:
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/$',views.Index.as_view(),name='index'), url(r'^index/(\d+)/',views.Index.as_view(),name='index'), ]
另一种方法,重写写另一个类,让他请求另一个类中的方法:
urls.py中
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/$',views.Index.as_view(),name='index'), url(r'^index/(\d+)/',views.Rindex.as_view(),name='index'), ]
views.py中
import json from django.shortcuts import render,HttpResponse from django import views from rest_framework.response import Response from web import models from rest_framework.views import APIView from rest_framework import serializers class CourseSerializer(serializers.ModelSerializer): class meta: model = models.Course fields = '__all__' #一对多 teacher_name = serializers.CharField(max_length=32, source='teacher.name') #多对多 student_name = serializers.SerializerMethodField() def get_student_name(self,obj): stu_list = obj.course.all() l=[] for stu in stu_list: d= {} d['name'] = stu.name l.append(d) return l class Index(APIView): def get(self,request): obj = models.Course.objects.all() CS = CourseSerializer(obj,many=True) print(CS.data) # return render(request,'index.html',{'res_list':res_list}) return Response(CS.data) def post(self,request): cs = CourseSerializer(data=request.data,many=False) if cs.is_valid(): models.Course.objects.create(**cs.data) print('保存成功啦') return Response(cs.data) return Response(cs.errors) class Rindex(APIView): def get(self,request,id): obj = models.Course.objects.get(id=id) CS = CourseSerializer(obj) return Response(CS.data) def put(self,request,id): obj = models.Course.objects.filter(id = id).first() CS = CourseSerializer(data=request.data,instance=obj) if CS.is_valid(): CS.save() #实际上这一步就是进行的update return Response(CS.data) return Response(CS.errors) def delete(self,request,id): obj = models.Course.objects.filter(id = id).delete() return Response('删除成功')
serializer的field都有哪些?
1.CharField 对应models.CharField,同时如果指定长度,还会负责校验文本长度。 max_length:最大长度; min_length:最小长度; allow_blank=True:表示允许将空串做为有效值,默认False; 2.EmailField 对应models.EmailField,验证是否是有效email地址。 3.IntegerField 对应models.IntegerField,代表整数类型 4.FloatField 对应models.FloatField,代表浮点数类型 5.DateTimeField 对应models.DateTimeField,代表时间和日期类型。 format='YYYY-MM-DD hh:mm':指定datetime输出格式,默认为DATETIME_FORMAT值。 需要注意,如果在 ModelSerializer 和HyperlinkedModelSerializer中如果models.DateTimeField带有auto_now=True或者auto_add_now=True,则对应的serializers.DateTimeField中将默认使用属性read_only=True,如果不想使用此行为,需要显示对该字段进行声明: class CommentSerializer(serializers.ModelSerializer): created = serializers.DateTimeField() class Meta: model = Comment 6.FileField 对应models.FileField,代表一个文件,负责文件校验。 max_length:文件名最大长度; allow_empty_file:是否允许为空文件; 7.ImageField 对应models.ImageField,代表一个图片,负责校验图片格式是否正确。 max_length:图片名最大长度; allow_empty_file:是否允许为空文件; 如果要进行图片处理,推荐安装Pillow: pip install Pillow 8.HiddenField 这是serializers中特有的Field,它不根据用户提交获取值,而是从默认值或可调用的值中获取其值。一种常见的使用场景就是在Model中存在user_id作为外键,在用户提交时,不允许提交user_id,但user_id在定义Model时又是必须字段,这种情况下就可以使用HiddenField提供一个默认值: class LeavingMessageSerializer(serializers.Serializer): user = serializers.HiddenField( default=serializers.CurrentUserDefault() )
serializer的公共参数
1.read_only read_only=True表示该字段为只读字段,即对应字段只用于序列化时(输出),而在反序列化时(创建对象)不使用该字段。默认值为False。 2.write_only write_only=True表示该字段为只写字段,和read_only相反,即对应字段只用于更新或创建新的Model时,而在序列化时不使用,即不会输出给用户。默认值为False。 3.required required=False表示对应字段在反序列化时是非必需的。在正常情况下,如果反序列化时缺少字段,则会抛出异常。默认值为True。 4.default 给字段指定一个默认值。需要注意,如果字段设置了default,则隐式地表示该字段已包含required=False,如果同时指定default和required,则会抛出异常。 5.allow_null allow_null=True表示在序列化时允许None作为有效值。需要注意,如果没有显式使用default参数,则当指定allow_null=True时,在序列化过程中将会默认default=None,但并不会在反序列化时也默认。 6.validators 一个应用于传入字段的验证函数列表,如果验证失败,会引发验证错误,否则直接是返回,用于验证字段,如: username = serializers.CharField(max_length=16, required=True, label='用户名', validators=[validators.UniqueValidator(queryset=User.objects.all(),message='用户已经存在')]) 7.error_message 验证时错误码和错误信息的一个dict,可以指定一些验证字段时的错误信息,如: mobile= serializers.CharField(max_length=4, required=True, write_only=True, min_length=4, label='电话', error_messages={ 'blank': '请输入验证码', 'required': '该字段必填项', 'max_length': '验证码格式错误', 'min_length': '验证码格式错误', }) 8.style 一个键值对,用于控制字段如何渲染,最常用于对密码进行密文输入,如: password = serializers.CharField(max_length=16, min_length=6, required=True, label='密码', error_messages={ 'blank': '请输入密码', 'required': '该字段必填', 'max_length': '密码长度不超过16', 'min_length': '密码长度不小于6', }, style={'input_type': 'password'}, write_only=True) 9.label 一个简短的文本字串,用来描述该字段。 10.help_text 一个文本字串,可用作HTML表单字段或其他描述性元素中字段的描述。 11.allow_blank allow_blank=True 可以为空 设置False则不能为空 12.source source='user.email'(user表的email字段的值给这值) 设置字段值 类似default 通常这个值有外键关联属性可以用source设置 13.validators 验证该字段跟 单独的validate很像 UniqueValidator 单独唯一 validators=[UniqueValidator(queryset=UserProfile.objects.all()) UniqueTogetherValidator: 多字段联合唯一,这个时候就不能单独作用于某个字段,我们在Meta中设置。 validators = [UniqueTogetherValidator(queryset=UserFav.objects.all(),fields=('user', 'course'),message='已经收藏')] 14.error_messages 错误消息提示 error_messages={ "min_value": "商品数量不能小于一", "required": "请选择购买数量" }) 15.ModelSerializers ModelSerializers继承于Serializer,相比其父类,ModelSerializer自动实现了以下三个步骤: 1.根据指定的Model自动检测并生成序列化的字段,不需要提前定义; 2.自动为序列化生成校验器; 3.自动实现了create()方法和update()方法。 使用ModelSerializer方式如下: class StudentSerializer(serializers.ModelSerializer): class Meta: # 指定一个Model,自动检测序列化的字段 model = StudentSerializer fields = ('id', 'name', 'age', 'birthday') 相比于Serializer,可以说是简单了不少,当然,有时根据项目要求,可能也会在ModelSerializer中显示声明字段,这些在后面总结。 model 该属性指定一个Model类,ModelSerializer会根据提供的Model类自动检测出需要序列化的字段。默认情况下,所有Model类中的字段将会映射到ModelSerializer类中相应的字段。
视图组件(mixins混合类)
from django.shortcuts import render,HttpResponse,redirect from django.views import View from app01 import models from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers #将序列化组件都放到一个单独的文件里面,然后引入进来 from app01.serializer import BookSerializers,PublishSerializers from rest_framework import generics from rest_framework.mixins import ListModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin # ListModelMixin 查看所有数据,对应着咱们上面的get查看所有数据的接口 # CreateModelMixin 添加数据的操作,封装了一个create操作,对应着上面的POST添加数据的几口 # UpdateModelMixin 更新 # DestroyModelMixin 销毁(删除) # RetrieveModelMixin 获取单条数据 # 我们自己提炼出,说,每个表的操作基本都是上面的get、post、delete、put操作,所以我们想将这几个方法提炼出来,将来供其他类来继承使用,那么drf帮我们封装好了,就是这几个Minin类 class PublishView(ListModelMixin,CreateModelMixin,generics.GenericAPIView): ''' GenericAPIView肯定继承了APIView,因为APIView里面的功能是我们必须的,而这个GenericAPIView是帮我们做衔接用的,把你的APIView的功能和我们的Minin类的功能衔接、调度起来的 ''' #继承完了之后,我们需要将我们前面各个表的序列化中提炼的两个不同的变量告诉咱的类,注意,下面的两个变量名就是他们俩,不能改,并且必须给 queryset = models.Publish.objects.all() serializer_class = PublishSerializers def get(self,request): ''' 分发找到对应的请求方法,就是咱的get方法,而处理数据的逻辑是继承的那个ListModelMixin类里面的list方法做了,所以我们只需要return self.list(request方法就行了,处理数据的逻辑就不要我们自己再写了 :param request: :return: ''' return self.list(request) #list方法帮我们做了序列化 #post方法添加一条数据,我们只需要执行一下CreateModelMixin类中的create方法就行了 def post(self,request): return self.create(request) class SPublishView(UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin,generics.GenericAPIView): #下面这两个变量和对应数据是必须给的,并且只能叫这个 queryset = models.Publish.objects.all() serializer_class = PublishSerializers # def get(self,request,id): #id就不需要传了,因为人家要求在url中添加的命名分组的pk参数自动来做了 def get(self, request, *args, **kwargs): #*args, **kwargs是为了接收url的那些参数的,咱们写的有个pk参数。 return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs)
注:最后一定要继承generics.GenericAPIView,这个类里面封装了APIView,否则不会进行分发
urls.py中
urlpatterns = [
#publish表的接口 url(r'^publishs/$', views.PublishView.as_view(),), #使用UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin这类Mixin类的时候,人家要求必须有个命名分组参数,名字叫做pk,名字可以改,但是先这样用 url(r'^publishs/(?P<pk>\d+)/', views.SPublishView.as_view(),), ]
generics中还封装了ListCreateAPIView,RetrieveUpdateDestroyAPIView两个类,
# ListCreateAPIView类就帮我们封装了get和create方法 class AuthorView(generics.ListCreateAPIView): queryset = models.Author.objects.all() serializer_class = AuthorSerializers # RetrieveUpdateDestroyAPIView这个类封装了put、get、patch、delete方法 class SAuthorView(generics.RetrieveUpdateDestroyAPIView): queryset = models.Author.objects.all() serializer_class = AuthorSerializers
还有更高级版的就是包含了所有方法的一个类,叫ModelViewSet
from rest_framework.viewsets import ModelViewSet #继承这个模块 class AuthorView(ModelViewSet): queryset = models.Author.objects.all() serializer_class = AuthorSerializers
此时urls.py中就要换写法了
url(r'^authors/$', views.AuthorView.as_view({"get":"list","post":"create"}),), url(r'^authors/(?P<pk>\d+)/', views.AuthorView.as_view({ 'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy' }),),
token认证
什么是token认证?
token认证其实和session差不多,就是浏览器往后端发送请求,后端接收到数据并校验成功之后生成一个随机的字符串,存在后端一份,前端一份,当下一次请求发送过来之后,会携带着这个token去后端数据库中查找,如果存在,说明已登录,不存在就说明没有登录。
token认证怎么实现?
首先我们要先建一个关于user的一对一关系的表
types = ((1,'VIP'),(2,'SVIP')) class User(models.Model): username = models.CharField(max_length = 32) passowrd = models.CharField(max_length = 32) type = models.IntegerField(choices=types, default=1) class User_Token(models.Model): user = models.OneToOneField('user') token = models.CharField(max_length = 32)
每次登陆后实现token自动刷新
import uuid # 用户登录认证 class UserAuth(APIView): RET = { 'code': None, 'userinfo': None, 'msg': None, 'token': None } def post(self, request): username = request.data.get('username') password = request.data.get('password') user_obj = models.User.objects.filter(username=username, password=password).first() if user_obj: random_str = uuid.uuid4() models.UserToken.objects.update_or_create( # 有就更新,没有就创建 user=user_obj, defaults={ 'token': random_str, } ) self.RET['code'] = 0 # 跟前端约定好,0表示成功 self.RET['userinfo'] = username self.RET['msg'] = 'success' self.RET['token'] = random_str else: self.RET['code'] = -1 self.RET['userinfo'] = username self.RET['msg'] = 'failure' return Response(self.RET)
实现ing:
from rest_framework.exceptions import AuthenticationFailed from rest_framework.authentication import BaseAuthentication from app01 import models # drf提供认证失败的异常 class UserAuth(BaseAuthentication): #每个认证类,都需要有个authenticate_header方法,并且有个参数request def authenticate_header(self,request): pass # authenticate方法是固定的,并且必须写这个名字,是request新对象 def authenticate(self, request): token = request.query_params.get('token') # 类似于request.GET.get('token') token_obj = models.UserToken.objects.filter(token=token).first() if token_obj: return 'tian', 'hoiagshdsilhhfkjbc' # 要么就返回两个值,要么就不返回 else: return AuthenticationFailed('认证失败')
将这个组件应用到视图函数中
class BookHandle(APIView): authentication_classes = [UserAuth, ] #一进到这个函数就进行验证,变量名必须是这个,认证组件需要从所在文件中导入 # 获取所有数据 def get(self, request):
print(request.user,request.auth) #得到的是 'tian','hoiagshdsilhhfkjbc' ,即上面返回的内容 book_obj_list = models.Book.objects.all() book_res = BookSerializer(book_obj_list, many=True) return Response(book_res.data)
注意:在settings.py中我们要写这么一个变量
REST_FRAMEWORK = { # 前面的key是固定的,后面的是验证组件的路径,直到组件名 'DEFAULT_AUTHENTICATION_CLASSES':[web.utils.auth.UserAuth,] }
权限组件
编写关于权限的类:
from rest_framework.permission import BasePermission class User_permission(BasePermission): message = 'SVIP才能访问' #变量名只能叫message,权限不够返回message中的信息 def has_permission(self,request,view): #view是用权限的视图函数 if request.user.usertype == '3': #判断user表中的权限 reutrn True # 通过权限 else: return False #没有通过权限
视图函数中
class Bookhandle(APIView): permission_classes = [User_permission,] def get(self,request): book_obj_list = models.Book.objects.all() book_res = BookSerializer(book_obj_list, many=True) return Response(book_res.data)
settings.py中
频率组件(限制访问次数,反爬)
rest_framework中内置的throttles类
from rest_framework.throttling import SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): scope="visit_rate" # 限制访问次数用的 def get_cache_key(self, request, view): # 这个方法是固定的 return self.get_ident(request)
视图函数中
class BookHandle(APIView): throttle_classes = [VisitThrottle, ] # 添加访问频率,需要从该类所在位置引入 # 获取所有数据 def get(self, request): book_obj_list = models.Book.objects.all() book_res = BookSerializer(book_obj_list, many=True) return Response(book_res.data)
settings.py中
REST_FRAMEWORK={ "DEFAULT_THORTTLE_RATES":{
#与throttles类中的scope值相同
"visit_rate":"5/m", #每分钟最多访问5次,单位可以改
}
}
分页组件
视图函数
from rest_framework.pagination import PageNumberPagination class Bookview(APIView): def get(self,request): book_list = models.Book.objects.all() #创建分页器对象 pagenum = PageNumberPagination() #通过分页器的pageinate_queryset方法进行分页 book_page_list=pagenum.paginate_quertset(book_list,request) book_res = BookSerialzer(book_page_list,many=True) return Response(book_res.data)
然后在settings.py中
RESTFRAME_WORK ={ #key必须叫这个名,value表示每页显示的条数 'PAGE_SIZE' : 2 }
当然了,也可以自定义page_size
from rest_framework.pagination import PageNumberPagination class MyPagination(PageNumberPagination): # 下面这些参数只能叫固定的名字 page_size = 3 #每页数据显示条数 page_query_param = 'pp' #http://127.0.0.1:8000/books/?pp=1,查询第一页的数据 page_size_query_param = 'size' # 用做自定义显示数据http://127.0.0.1:8000/books/?pp=2&size=5 #正常是显示3个,这样写就显示5个 max_page_size = 10 #最大每页展示多少条,即便是你前端通过page_size_query_param临时调整了page_size的值, 但是最大也不能超过我们设置的max_page_size的值 class BookHandle(APIView): # 获取所有数据 def get(self, request): book_obj_list = models.Book.objects.all() pagenum = MyPagination() page_book_list = pagenum.paginate_queryset(book_obj_list, request) book_res = BookSerializer(page_book_list, many=True) return Response(book_res.data)
继承modelviewset使用分页器
from rest_framework.viewsets import ModelViewSetfrom rest_framework.pagination import PageNumberPagination
class MyPagination(PageNumberPagination): page_size = 3 page_query_param = 'pp' page_size_query_param = 'size' max_page_size = 10 class CAuthorHandle(ModelViewSet): queryset = models.Author.objects.all() # 变量名必须是这个 serializer_class = AuthorSerializer # 变量名必须是这个 pagination_class = MyPagination #配置我们自己写的分页类
url注册器
使用前提:必须是modelviewset序列化组件才能使用
from rest_framework.viewsets import ModelViewSet class AuthorView(ModelViewSet): queryset = models.Author.objects.all() serializer_class = AuthorSerializers class BookView(ModelViewSet): queryset = models.Book.objects.all() serializer_class = BookSerializers
不怎么常用,一般都是自己写,但还是要知道,就是自动生成4个url
from django.conf.urls import url,include from django.contrib import admin from app01 import views from rest_framework import routers
#实例化url对象 router = routers.DefaultRouter() #自动帮我们生成四个url router.register(r'authors', views.AuthorView) router.register(r'books', views.BookView) urlpatterns = [ url(r'', include(router.urls)), #http://127.0.0.1:8000/books/ 也可以这样写:http://127.0.0.1:8000/books.json/,此时拿到的是json数据 #登陆认证接口 url(r'^login/$', views.LoginView.as_view(),), #别忘了$符号结尾 ]