django作跨域同源 须要把csrf去掉 跨站请求伪造javascript
同源机制:域名、协议、端口号相同的同源
不写头部请求 跨域会拦截报错缺乏请求信息 (1) 请求方法是如下三种方法之一:(也就是说若是你的请求方法是什么put、delete等确定是非简单请求) HEAD GET POST (2)HTTP的头信息不超出如下几种字段:(若是比这些请求头多,那么必定是非简单请求) Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain,也就是说,若是你发送的application/json格式的数据,那么确定是非简单请求,vue的axios默认的请求体信息格式是json的,ajax默认是urlencoded的。 #vue.js axios -- $.ajax ajax和axios都是基于js的封装框架 支持跨域,简单请求 服务器设置响应头:Access-Control-Allow-Origin = '域名' 或 '*'
不在简单范围内的请求头和请求方法css
access-contorl-allow-origin 请求头 写了斜杠http://127.0.0.1/ 只能路径以后才能够访问 直接写ip地址http://127.0.0.1 是全路径下 复杂请求有option请求 进行预警’ contype是规定以什么格式进行上传 支持跨域,复杂请求 因为复杂请求时,首先会发送“预检”请求'options'请求方法,若是“预检”成功,则发送真实数据。 “预检”请求时,容许请求方式则需服务器设置响应头:Access-Control-Request-Method “预检”请求时,容许请求头则需服务器设置响应头:Access-Control-Request-Headers res['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8001' res['Access-Control-Allow-Headers'] = 'content-type'#请求头部信息 # res['Access-Control-Allow-Methods'] = 'PUT'#请求方法 # res['Access-Control-Allow-Origin'] = '* 所有网址均可以进行访问 请求头部信息是须要按照 指定的要求文件格式
GIT版本管理工具html
集中式的版本管理工具 是把全部项目上线到项目中 集中管理 蹦了以后项目就崩了前端
分布式 能够直接把整个版本down下来进行开发 可是你开发的东西基于其余人就没办法进行操做 又要等着上传vue
1.经常使用vue.js 2.后端给前端json数据 3.后端要想使用drf组件**request.data 4.须要前端返回json数据类型 5.self封装了request属性 self.request.method
移动端盛行java
crm项目,前端后端一块儿写,运行在浏览器上
第二部分 任务python
http://127.0.0.1:8000/info/get/ http://127.0.0.1:8000/info/add/ http://127.0.0.1:8000/info/update/ http://127.0.0.1:8000/info/delete/
http://127.0.0.1:8000/info/ get,获取数据 post,添加 put,更新 delete,删除
restful是什么 resultful是一种先后端约定俗称的一种规则,用于程序之间进行数据交换的约定 详细说明 1。url中通常用名词:http:www。baidu.com/article/面向资源编程,网络上东西都视为资源 1.5筛选条件在url参数中进行传递例如 #http://www.baidu.com/article/?page=1&category 2.是一套规则,用于程序之间进行数据交换的约定。 3.他规定了一些协议,对咱们感觉最直接的的是,之前写增删改查须要写4个接口,restful规范的就是1 个接口,根据method的不一样作不一样的操做,好比: 4.get/post/delete/put/patch/delete. 初次以外,resetful规范还规定了: - 数据传输经过json 扩展:先后端分离、app开发、程序之间(与编程语言无关) 5- URL中通常用名词: http://www.luffycity.com/article/ (面向资源编程,网络上东西都视为资源) 6建议加上api标识 url写法 http://www.luffycity.com/api/v1....(建议,由于他不会存在跨域的问题) 注意:版本还能够放在请求头中 http://www.luffycity.com/api/ accept: ... 7建议用https代替http #为了保证数据的安全 9.要返回给用户状态码(建议) from rest_formwork import status status.HTTP_200_OK 返回状态码 或者在字典里面返回自定义code状态码 #例data{ # code:10000 status:radom_string #} - 200,成功 - 300,301永久 /302临时 - 400,403拒绝 /404找不到 - 500,服务端代码错误 10新增的数据返回值(建议) 要返回多个列表套字典格式 GET HTTP:..WWW.xxx.com/api/user/ [ {"id":1,"name":"xxx","age":19} {"id":1,name} ] 单条返回字典 11 操做异常要返回错误信息 { error:"Invalid API key" } 12 对于下一个请求要返回一些接口:Hypermedia AP { 'id':2, 'name':'alex', 'age':19, 'depart': "http://www.luffycity.com/api/user/30/" }
JSON: { name:'alex', age:18, gender:'男' }
之前用webservice,数据传输格式xml。 XML <name>alex</name> <age>alex</age> <gender>男</gender>
使代码更加专业mysql
django建立一个项目叫作api '接口' 经过jsonresopnese(data,safe=Flase) jsonresopnese默认支持字典 非字典须要使用safe
约定俗成的返回数据要返回json类型jquery
只使用一个url cbv格式ios
#只使用一个url cbv格式 get 获取数据 post添加 put更新 patch局部更新 delete删除 请求方法
基于django能够实现遵循restful规范的接口开发规范
drf是一个基于django开发的组件,本质是一个django的app。 drf能够办咱们快速开发出一个遵循restful规范的程序
写接口的时候用到drf
from django.views import View 以前django是继承view 继承apiview from rest_framework import Apiview from rest_framework.views import APIView
继承apiview 1.请求来了先执行视图的dispatch方法 2.版本处理 3.认证 4.权限 5.节流 6.解析器 ''' -解析器,根据用户请求体格式不一样进行数据解析,解析以后放在request.data中 若是content-type:x-www-urlencoded,那么drf会根据 & 符号分割的形式去处理请 求体。 user=wang&age=19 若是content-type:application/json,那么drf会根据 json 形式去处理请求体。 {"user":"wang","age":19} ''' 7.序列化 ''' - 序列化serlizer类 .data,能够对QuerySet进行序列化,也能够对用户提交的数据进行校验。 ''' 8.筛选器 9.分页 10.渲染器 - 渲染器,能够帮咱们把json数据渲染到页面上进行友好的展现。(内部会根据请求设备不一样作不一样的 展现)
pip3 install djangorestframework
若是有新建的app也须要进行注册,要否则不识别
admin不会自动加载
model数据库表结构也不会执行同步
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework' ]
from django.conf.urls import url from django.contrib import admin from api import views urlpatterns = [ url(r'^drf/info/', views.DrfInfoView.as_view()), ] #as_view()执行as_view方法和函数同样执行
而后进行引用 模块
内置response代替了 django的jsonresponse
from django.shortcuts import render from rest_framework.response import Response from rest_framework.views import APIView from hula import models # Create your views here. class DrfCategoryView(APIView): def post(self, request, *args, **kwargs): #接收 有名传参(关键字传参) 无名传参(位置传参) obj = request.POST.get('data') if obj: models.Category.objects.create(**obj) return Response('OK成功')
#基于cbv操做 也就是视图类 1.给别人提供一个URL,根据URL请求方式的不一样,作不一样操做。 get,获取 post,增长 put,所有更新 patch,局部更新 delete,删除 2.数据传输基于json格式。 潜规则 由于不论什么请求方式,都须要给前端返回对象内容,就是json格式的
若是出现 NOTmplementedError错误 说明必须实现实现发 方法
不基于drf也能够是实现resful规范来开发接口程序 使用drf以后,能够快速帮咱们开发resful规范来开发接口
外键须要使用为空或者给一个默认值 blank=True Null=True
from django.conf.urls import url from django.contrib import admin from api import views urlpatterns = [ url(r'^drf/category/', views.DrfCategoryView.as_view()), ]
from rest_framework.views import APIView from rest_framework.response import Response class DrfCategoryView(APIView): pass
接口:访问接口时,建立一个文章类型(post增长)
from django.shortcuts import render from rest_framework.response import Response from rest_framework.views import APIView from hula import models # Create your views here. class DrfCategoryView(APIView): def post(self, request, *args, **kwargs): #接收 有名传参(关键字传参) 无名传参(位置传参) obj = request.POST.get('data') if obj: models.Category.objects.create(**obj) return Response('OK成功')
假设写后端
http://127.0.0.1:8000/drf/category/
拼接成 value&value
requesr.body获取请求体 request.POST获取的是POST请求的全部数据 #字典类型 #QueryDict: {'title': ['摩擦'], 'id': ['1']}> request.body: name=alex&age=19&gender=12 request.POST: {'name': ['alex'], 'age': ['19'], 'gender': ['12']}
#request.body获取是bytes类型 request.body: b'{"id":1,"name":"ALEX","AGE"}' 1.decode解码成json字符串 2.loads成能够给python处理的字典(json序列化) json.loads(request.body.decode('utf-8')) #json格式字符串是双引号
requesr.body获取请求体里面的数据 request.POST获取的是POST请求的全部数据 request.body: b'{"ID":1,"name":"Alex","age":19}' request.POST: 没有值
from django.forms import model_to_dict model_to_dict#转换 obj = models.Category.objects.filter(pk=pk).first() obj=model_to_dict(obj)
默认封装了 request.data(django没有) 进行了解码序列化
视图 APIview request.data中有一个解析器 解析器,根据用户请求体格式不一样进行数据解析,解析以后方法 在进行解析时候,drf会读取http
路径
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^info/$', views.InfoView.as_view()), url(r'^drf/info/$', views.DrfInfoView.as_view()), url(r'^drf/category/$', views.DrfCategoryView.as_view()), url(r'^drf/category/(?P<pk>\d+)/$', views.DrfCategoryView.as_view()), ]
class DrfCategoryView(APIView): def get(self,request,*args,**kwargs): #obj=models.Article.objects.values() #obj=models.Article.objects.all() # obj=models.Article.objects.all.values('id','name') obj=list(obj) return Response(obj)
?format=json 能够直接序列化json
不使用序列化器
from django.forms import model_to_dict model_to_dict把model对象转换成字典 def get(self,request,*args,**kwargs): pk=kwargs.get('pk') # print(1) if not pk:#判断有没有取到值 obj=models.Article.objects.values() # obj=models.Article.objects.all.values('id','name') obj=list(obj) return Response(obj) else: print(1) obj = models.Article.objects.filter(pk=pk).first() obj=model_to_dict(obj) return Response(obj)
from api import models from django.forms.models import model_to_dict class DrfCategoryView(APIView): def delete(self,request,*args,**kwargs): """删除""" pk = kwargs.get('pk') models.Category.objects.filter(id=pk).delete() return Response('删除成功')
request.data 能够进行序列化和解码 序列化和编码过程 json格式字符串是双引号 request.body: b'{"id":1,"name":"ALEX","AGE"}' 先进行解码 json序列化 json.loads(request.body.decode('utf-8')) request.data能够接受json 和 &的数据
from api import models from django.forms.models import model_to_dict class DrfCategoryView(APIView): def put(self,request,*args,**kwargs): """更新""" pk = kwargs.get('pk') models.Category.objects.filter(id=pk).update(**request.data) return Response('更新成功')
from api import models from django.forms.models import model_to_dict class DrfCategoryView(APIView): def post(self,request,*args,**kwargs): """增长一条分类信息""" #利用model对象之间点create 须要一条一条添加 #所以须要打散字典变成关键字形式 models.Category.objects.create(**request.data) return Response('成功')
查看正确的值 is.vild() print(ser.validated_data) 多种状况使用多个serileiter进行区分 随机应变 是一个类 #知识点 1.model 指定哪个model 2.fields 表示须要序列化的字段,"__all__"所有字段 3.depth 向下查找一层。指对外键关系会继续遍历外键对象的所有属性。 category=serializers.CharField(source='get_字段_display',required=False) source能够自动查询是否是可执行的 自动加括号() 展现特殊的数据(choices、FK、M2M)可以使用 depth source,无需加括号,在源码内部会去判断是否可执行,若是可执行自动加括号。【fk/choice】 SerializerMethodField,定义钩子方法。【m2m】
data=request.data 指定关键字传参 把提交的数据增长到数据库中 from rest_framework import serializers class NewCategorySerializer(serializers.ModelSerializer): class Meta: model = models.Category # fields = "__all__" fields = ['id','name'] def get_date(self,obj): return obj.date.strftime('%Y-%m-%d %H:%M') 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 delete(self, request, *args, **kwargs): pk = kwargs.get("pk") if pk: obj=models.Article.objects.filter(pk=pk).delete() print(obj) return Response('删除成功') return Response('删除失败')
def put(self,request,*args,**kwargs): pk = kwargs.get('pk') category_object = models.Category.objects.filter(id=pk).first() ser = NewCategorySerializer(instance=category_object,data=request.data) if ser.is_valid(): ser.save() return Response(ser.data) return Response(ser.errors)
1.ser.data能够把quest类型变为字典 ser.data 2.序列化对象 instance=对应的旧值 3.many=true容许查询多个表必须指定的值 NewCategorySerializer(instance=queryset,many=True)
class NewCategoryView(APIView): def get(self,request,*args,**kwargs): pk = kwargs.get('pk') #多条数据查询 if not pk: queryset = models.Category.objects.all() ser = NewCategorySerializer(instance=queryset,many=True) return Response(ser.data) else: #单条数据查询 model_object = models.Category.objects.filter(id=pk).first() ser = NewCategorySerializer(instance=model_object, many=False) return Response(ser.data)
1.model 指定哪个model 2.fields 表示须要序列化的字段,"__all__"所有字段 3.depth 向下查找一层。指对外键关系会继续遍历外键对象的所有属性。 category=serializers.CharField(source='get_字段_display',required=False) source能够自动查询是否是可执行的 自动加括号() 展现特殊的数据(choices、FK、M2M)可以使用 depth source,无需加括号,在源码内部会去判断是否可执行,若是可执行自动加括号。 【fk/choice】 SerializerMethodField,定义钩子方法。【m2m】 #datetime数据类型的显示 定义一个钩子 date=serializers.SerializerMethodField() def get_date(self,obj): return obj.date.strftime(%Y-%m-%d %H:%M:%S) if obj.date else ""
from rest_framework import serializers from api import models 使用这个 class ArticleSerializer(serializers.ModelSerializer): source='属性.跨表字段' #名字能够写成和model字段同样用于覆盖 字段名对应的值 category=serializers.CharField(source='category.name',required=False) class Meta: model = models.Article fields = "__all__" #若是要写fields="__all__" #须要指定字段 是从获取字段名对应的值 def get_x1(self,obj): return obj.category.name #查出来对象 能够点属性 source能够自动查询是否是可执行的 自动加括号()
获取choice选择框对应值 from rest_framework import serializers from api import models 获取对应的列表的名称 class ArticleSerializer(serializers.ModelSerializer): #第一种 status_txt = serializers.CharField(source='get_status_display',required=False) #第二种 x2 = serializers.SerializerMethodField()#相似一个钩子 def get_x2(self,obj): return obj.get_status_display() #跨表 class ArticleSerializer(serializers.ModelSerializer): x1 = serializers.SerializerMethodField() class Meta: model = models.Article # fields = "__all__" fields = ['id','title','summary','content','category','category_txt','x1','status','status_txt','x2'] #若是放入fields #若是不写required=False或者read_onlye=False 会进行判断不为空 def get_x1(self,obj) #obj当前表的对象 #return 对象.字段属性.跨表字段 return obj.category.name 多对多 SerializerMethodField,定义钩子方法。【m2m】 class NewArticleSerializer(serializers.ModelSerializer): tag_info = serializers.SerializerMethodField() class Meta: model = models.Article fields = ['title','summary','tag_info'] #钩子取出每个字典 def get_tag_info(self,obj): return [row for row in obj.tag.all().values('id','title')] class FormNewArticleSerializer(serializers.ModelSerializer): class Meta: model = models.Article fields = '__all__'
使用patch
partial=True#容许部分更新 partial=True def patch(self,request,*args,**kwargs): """局部""" pk = kwargs.get('pk') article_object = models.Article.objects.filter(id=pk).first() ser = serializer.ArticleSerializer(instance=article_object, data=request.data,partial=True) if ser.is_valid(): ser.save() return Response(ser.data) return Response(ser.errors)
class NewArticleSerializer(serializers.ModelSerializer): tag_info = serializers.SerializerMethodField() class Meta: model = models.Article fields = ['title','summary','tag_info'] def get_tag_info(self,obj): return [row for row in obj.tag.all().values('id','title')] class FormNewArticleSerializer(serializers.ModelSerializer): class Meta: model = models.Article fields = '__all__'
class NewArticleView(APIView): def get(self,request,*args,**kwargs): pk = kwargs.get('pk') if not pk: queryset = models.Article.objects.all() ser = serializer.NewArticleSerializer(instance=queryset,many=True) return Response(ser.data) article_object = models.Article.objects.filter(id=pk).first() ser = serializer.NewArticleSerializer(instance=article_object, many=False) return Response(ser.data) def post(self,request,*args,**kwargs): ser = serializer.FormNewArticleSerializer(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') article_object = models.Article.objects.filter(id=pk).first() ser = serializer.FormNewArticleSerializer(instance=article_object, data=request.data) if ser.is_valid(): ser.save() return Response(ser.data) return Response(ser.errors) def patch(self,request,*args,**kwargs): """局部""" pk = kwargs.get('pk') article_object = models.Article.objects.filter(id=pk).first() ser = serializer.FormNewArticleSerializer(instance=article_object,data=request.data,partial=True) 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.Article.objects.filter(id=pk).delete() return Response('删除成功')
用于固定页面显示
获取多条数据
"count": 54,#每页显示多少条数据 #下一页 "next": "http://127.0.0.1:8000/drf/article/?page=2", #上一页 "previous": null,
类的约束
必须写指定的功能 # 约束子类中必须实现f1 class Base(object): def f1(self): raise NotImplementedError('asdfasdfasdfasdf') class Foo(Base): def f1(self): print(123) obj = Foo() obj.f1()
引用模块 from rest_framework.pagination import PageNumberPagination#引用分页模块 from rest_framework import serializers#引用序列化模块 class pagesize(PageNumberPagination): page_size=1 class PageArticleView(APIView): def get(self,request,*args,**kwargs): queryset=models.Article.objects.all() page_obj = pagesize()#实例化一个页面大小 #进行分页 result = page_obj.paginate_queryset(queryset, request, self) #把分页的数据写入序列化器 ser = PageArticleerializer(instance=result, many=True) return page_obj.get_paginated_response(ser.data)
REST_FRAMEWORK = { "PAGE_SIZE":2 } class PageArticleView(APIView): def get(self,request,*args,**kwargs): queryset=models.Article.objects.all() page_obj=PageNumberPagination() result=page_obj.paginate_queryset(queryset,request,self) ser=PageArticleerializer(instance=result,many=True) return Response(ser.data)
offset 0 limit 1 从offset开始数limit条数 用于滑动灵活运用 能够限制max_limit=2 from rest_framework.pagination import PageNumberPagination from rest_framework.pagination import LimitOffsetPagination from rest_framework import serializers class PageArticleSerializer(serializers.ModelSerializer): class Meta: model = models.Article fields = "__all__" #重写父类 class HulaLimitOffsetPagination(LimitOffsetPagination): max_limit = 2 class PageArticleView(APIView): def get(self,request,*args,**kwargs): queryset = models.Article.objects.all() page_object = HulaLimitOffsetPagination() result = page_object.paginate_queryset(queryset, request, self) ser = PageArticleSerializer(instance=result, many=True) return Response(ser.data)
url(r'^page/view/article/$', views.PageViewArticleView.as_view()),
from rest_framework.generics import ListAPIView class PageViewArticleSerializer(serializers.ModelSerializer): class Meta: model = models.Article fields = "__all__" class PageViewArticleView(ListAPIView): queryset = models.Article.objects.all() #指定类 serializer_class = PageViewArticleSerializer
#经过源码里面的配置进行分页的选择 REST_FRAMEWORK = { "PAGE_SIZE":2, #分页的选择 "DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination" }
CMS系统格式
内容管理系统。CMS一般用做企业的数字信息管理系统,
功能的实现
1 增长文章(能够不写) #编写人员进行撰写 写入数据库 2 文章列表 3 文章详细 4 评论列表
1.没有变化的类型不须要建立一张表 2.数据量大的相似详细内容 须要另外开辟一张表,若是表中列(字段)太多 水平分表 进行一对一 3.图片路径存入数据库 4.自关联具备相同数据的表 #不是必须的 能够不须要写外键提升查询效率
urlpattent+=[]#为了区分增长的路径 from django.conf.urls import url from api import views urlpatterns = [ url(r'^Atricle/$', views.AtricleView.as_view()), ] urlpatterns+=[ url(r'^Atricle/(?P<pk>\d+)/$', views.AtricleView.as_view()), url(r'^Comment/$', views.CommentView.as_view()), url(r'^Comment/(?P<pk>\d+)/$', views.CommentView.as_view()), ]
from django.db import models class UserInfo(models.Model): """ 用户表 """ username = models.CharField(verbose_name='用户名',max_length=32) password = models.CharField(verbose_name='密码',max_length=64) class Article(models.Model): """ 文章表 """ category_choices = ( (1,'咨询'), (2,'公司动态'), (3,'分享'), (4,'答疑'), (5,'其余'), ) category = models.IntegerField(verbose_name='分类',choices=category_choices) title = models.CharField(verbose_name='标题',max_length=32) image = models.CharField(verbose_name='图片路径',max_length=128) # /media/upload/.... summary = models.CharField(verbose_name='简介',max_length=255) comment_count = models.IntegerField(verbose_name='评论数',default=0) read_count = models.IntegerField(verbose_name='浏览数',default=0) author = models.ForeignKey(verbose_name='做者',to='UserInfo') date = models.DateTimeField(verbose_name='建立时间',auto_now_add=True) class ArticleDetail(models.Model): article = models.OneToOneField(verbose_name='文章表',to='Article') content = models.TextField(verbose_name='内容') class Comment(models.Model): """ 评论表 """ article = models.ForeignKey(verbose_name='文章',to='Article') content = models.TextField(verbose_name='评论') user = models.ForeignKey(verbose_name='评论者',to='UserInfo') # parent = models.ForeignKey(verbose_name='回复',to='self', null=True,blank=True)
知识点补充
1.#序列化器写法 exclude=['字段']去除不进行校验 2.#序列化器内部的值 ser是序列化器实例化的对象 ser=序列化器( 1.instance=旧值(查询的值,数据库本类就有的) 2.data=要添加的值,提交的值(post请求,put,patch请求提交的值) ) 3.查询的时候 须要指定many=True 查询多个值必需要指定 ser.is_valid(): ''' 注意只打印存在字段的校验, 传入多的值不会进行打印和数据库的写入 ser.errors是全部字段校验错误的信息 ''' print(ser.validated_data)#至关于modelform form校验的值 ser.save()#存入数据库 括号里能够写值 由其余字段传入的 #article=ser.save()#aricle是新增这条数据的对象
多个序列化器
ser=AtricleSeriALIZER(data=request.data)
ser_detail=ArtcleDetaili(data=request.data)
if ser.is_valid() and ser_detail.is_valid()
article=ser.save(author=1)
ser_detail.save(article=article)
#能够等于对象=对象
#也可等于id=id(内容回顾)
ser_detail.save(article.id=article.id)
```
(编写数据的人员去写)
1.多个serlizer序列化器进行保存 2.save里面能够添加参数 能够添加对应字段的id和对象类型的数据 没有办法直接添加外键对应的值 能够经过save(对应的值或者对象) 3.序列化的对象就是对应文章的对象 4.做者是登录成功存入session的值 不须要手动传入
exclude=['author']#去除做者不进行校验 #为了把字段写活request.session获取值进行匹配取得匹配的文章 #查询对应文章对象不能直接post写死 由于不知道对应的文章 class AtricleSerializers(serializers.ModelSerializer): class Meta: model = models.Article exclude = ['author', ]#外键字段不进行校验 #request.session 登录用户明进行匹配 class AtricledetailSerializer(serializers.ModelSerializer): class Meta: model = models.ArticleDetail exclude = ['article', ] #外键字段不进行校验 2#文章对象或者文章值来肯定 来肯定添加对应的文章详细信息
两个序列化器进行校验
def post(self, request, *args, **kwargs): ser = AtricleSerializers(data=request.data) serDetail = AtricledetailSerializer(data=request.data) print(request.data) if ser.is_valid() and serDetail.is_valid(): # 由于做者id是根据登录的id进行存储(例如session) print(ser.validated_data) atricle = ser.save(author_id=1) serDetail.save(article=atricle) return Response(f'{ser.data}{serDetail.data}') return Response('错误')
先添加在显示
class AtiricleliST(serializers.ModelSerializer): class Meta: model = models.Article fields = "__all__"
多条数据 1.先进行实例化分页 2.把查询的数据进行分页, 3.放入序列化器里面进行处理显示 def get(self, request, *args, **kwargs): pk=kwargs.get("pk") if not pk: Article_obj = models.Article.objects.all().order_by('-date') Page_obj = PageNumberPagination() Page = Page_obj.paginate_queryset(Article_obj, request, self) print(Page) ser = AtiricleliST(instance=Page, many=True) print(3) return Page_obj.get_paginated_response(ser.data)
所有:http://127.0.0.1:8000/hg/article/ 筛选:http://127.0.0.1:8000/hg/article/?category=2
#筛选文章的数据 class ArticleView(APIView): """ 文章视图类 """ def get(self,request,*args,**kwargs): """ 获取文章列表 """ pk = kwargs.get('pk') if not pk: condition = {}#存储对应筛选 category = #从前端url获取值 request.query_params.get('category') if category:#若是由就进行过滤 condition['category'] = category queryset = #**condition 字典打散而后进行筛选 分类为这个id的 models.Article.objects.filter(**condition).order_by('-date') pager = PageNumberPagination() result = pager.paginate_queryset(queryset,request,self) ser = ArticleListSerializer(instance=result,many=True) return Response(ser.data) article_object = models.Article.objects.filter(id=pk).first() ser = PageArticleSerializer(instance=article_object,many=False) return Response(ser.data)
#去除对应的外键关联字段的验证article #只有文章建立了以后才能建立文章详情 #author对应的是用户id值,例如session #由于文章详细不能直接建立须要经过文章对象关联建立 class AtricledetailSerializer(serializers.ModelSerializer): class Meta: model = models.ArticleDetail exclude = ['article', ] class AtricleSerializers(serializers.ModelSerializer): class Meta: model = models.Article exclude = ['author', ]
查询一条数据的详细
view视图写法
def get(self, request, *args, **kwargs): pk=kwargs.get("pk") if not pk: Article_obj = models.Article.objects.all().order_by('-date') Page_obj = PageNumberPagination() Page = Page_obj.paginate_queryset(Article_obj, request, self) print(Page) ser = AtiricleliST(instance=Page, many=True) print(3) return Page_obj.get_paginated_response(ser.data) #正文开始 else是一条 if是多条 else: Article_obj=models.Article.objects.filter(pk=pk).first() #使用序列化处理一条数据 ser=AtiricleDetail(instance=Article_obj,many=False) return Response(ser.data) ································· 文章详细 文章表反向小写表名查询详细表 Article_obj=models.Article.objects.filter(pk=pk).first() 属性.小写表名.外键关联表属性 print(Article_obj.articledetaili.content)
http://127.0.0.1:8000/hg/comment/?article=2 request.query_params.get('article') #至关于request.GET。get 获取url的值 query_params获取?后面值 reqeust.data 至关于post
class MmentSerialiser(serializers.ModelSerializer): class Meta: model=models.Comment fields="__all__"
经过前端发送url进行筛选 # http://127.0.0.1:8000/hg/comment/?article=2 def get(self,request,*args,**kwargs): ''' request.query_params.get() 至关于request.GET.get() ''' coment=request.query_params.get('article') coment_obj=models.Comment.objects.filter(article_id=coment) show_coment=MmentSerialiser(instance=coment_obj,many=True) return Response(show_coment.data)
#去掉对应外键字段硬不须要进行关联 #request.session能够直接获取登录的用户名 class CommentSerialiser(serializers.ModelSerializer): class Meta: model=models.Comment exclude=['user']
锁定单个文章的评论增长
#返回数据的写法 #第一种 http://127.0.0.1:8000/hg/comment/ { article:1, content:'xxx' } #第二种 http://127.0.0.1:8000/hg/comment/?article=1 { content:'xxx' }
#文章由前端直接返回由于只有有了文章才能进行评论 #经过前端数据进行返回 def post(self,request,*args,**kwargs): ser=CommentSerialiser(data=request.data) if ser.is_valid(): print(ser.validated_data) ser.save(user_id=1) return Response(ser.data) print(ser.errors)
对查询出来的数据进行筛选可写可不写
from rest_framework.filters import BaseFilterBackend 源码 ''' def filter_queryset(self, request, queryset, view): #继承了这个类必须写这个方法否则报错 raise NotImplementedError(".filter_queryset() must be overridden.") ''' view是当前视图self queryset是筛选以后的数据 request 就是request def filter_queryset(self,request,queryset,view): pass
对查询出来的数据进行筛选可写可不写 #第一部分 from rest_framework.filters import BaseFilterBackend #继承类 class MyFilterBack(BaseFilterBackend): def filter_queryset(self,request,queryset,view): val = request.query_params.get('cagetory') return queryset.filter(category_id=val) #先实例化一个 class Indexview(APIview): def get(self,request,*arg,**kwargs): #就是查询一个表不理他 queryset=models.News.objects.all() #实例化一个类对象 obj=MyFilterBack() #传值顺序request,queryset,self result=obj.filter_queryset(request,queryset,self) return Response('...')
def get(self,request,*args,**kwargs): # 1.获取单挑数据再作序列化 result = super().get(request,*args,**kwargs) # 2.对浏览器进行自加1 pk = kwargs.get('pk') models.Article.objects.filter(pk=pk).update(read_count=F('read_count')+1) # 3.对浏览器进行自加1 # instance = self.get_object() # instance.read_count += 1 # instance.save() return result
提供了公共方法 request.data 是request.post的封装 json序列化 dispatch()分发 self封装了request属性 self.request.method
@classmethod def as_view(cls, **initkwargs): if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet): def force_evaluation(): raise RuntimeError( 'Do not evaluate the `.queryset` attribute directly, ' 'as the result will be cached and reused between requests. ' 'Use `.all()` or call `.get_queryset()` instead.' ) cls.queryset._fetch_all = force_evaluation #执行父类的as_view方法 view = super().as_view(**initkwargs) #执行父类的super().as_view(**initkwargs) view.cls = cls view.initkwargs = initkwargs #闭包 csrf_exempt免除csrftoken的认证 return csrf_exempt(view) -------------------------------------- @classonlymethod def as_view(cls, **initkwargs): """ Main entry point for a request-response process. """ for key in initkwargs: if key in cls.http_method_names: raise TypeError("You tried to pass in the %s method name as a " "keyword argument to %s(). Don't do that." % (key, cls.__name__)) if not hasattr(cls, key): raise TypeError("%s() received an invalid keyword %r. as_view " "only accepts arguments that are already " "attributes of the class." % (cls.__name__, key)) def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs #执行本类的self.dispatch return self.dispatch(request, *args,**kwargs) #执行view return view -------------apiview的dispatch-------- def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs #封装老值request request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
#一 初识 from rest_framework.generics import GenericAPIView GenericAPIview继承了APIview class NewView(GenericAPIview): querset=model.News.objects.all() def get(self,request,*arg,**kwargs): #self对应NewView的对象 self.filter_queryset(self.queryset) 1.执行对应的filter_queryset方法 2.本类没有去寻找父类GenericAPIview ''' def filter_queryset(self, queryset): for backend in list(self.filter_backends): queryset = backend().filter_queryset(self.request, queryset, self) return queryset '''#因此默认返回原来的queryset 3.寻找filter_backends ''' filter_backends = api_settings.DEFAULT_FILTER_BACKENDS filter_backends是空的 '''
from rest_framework.generics import GenericAPIView #GenericAPIview继承了APIview class NewFiltrBackend(BaseFilterBackend): def filter_queryset(self,request,queryset,view): val = request.query_params.get('cagetory') return queryset.filter(category_id=val) class NewView(GenericAPIview): querset=model.News.objects.all() filter_backends=[NewFiltrBackend ,] def get(self,request,*arg,**kwargs): #self对应NewView的对象 v=self.get_queryset() queryset=self.filter_queryset(v) 1.执行对应的filter_queryset方法 2.本类没有去寻找父类GenericAPIview ''' 源码 继承类不写self.queryset会报错 assert self.queryset is not None, ( "'%s' should either include a `queryset` attribute, " "or override the `get_queryset()` method." % self.__class__.__name__ ) ''' ''' def filter_queryset(self, queryset): for backend in list(self.filter_backends): queryset = backend().filter_queryset(self.request, queryset, self) return queryset '''4.backend等于对应NewFiltrBackend类名() 实例化对象 ·· 执行NewFiltrBackend里面的filter_queryset方法 3.寻找filter_backends,本类的filter_backends #filter_backends=[NewFiltrBackend ,] 5.queryset=self.filter_queryset(self.queryset)是筛选以后的结果 #v=self.get_queryset() queryset=self.filter_queryset(v) 6.寻找对应get_queryset ''' 源码 queryset = self.queryset if isinstance(queryset, QuerySet): # Ensure queryset is re-evaluated on each request. queryset = queryset.all() return queryset '''#返回等于筛选以后的queryset #queryset=self.filter_queryset(queryset) #queryset=get_queryset() ·········self.get_serializer()··············· class Newserializers(serializers.ModelSerializer): class Meta: model=models.News fields="__all__" class NewFiltrBackend(BaseFilterBackend): def filter_queryset(self,request,queryset,view): val = request.query_params.get('cagetory') return queryset.filter(category_id=val) class NewView(GenericAPIview): querset=model.News.objects.all() filter_backends=[NewFiltrBackend ,] def get(self,request,*arg,**kwargs): #self对应NewView的对象 v=self.get_queryset() queryset=self.filter_queryset(v) self.get_serializer()#本类没有去父类找 #代替了 ser=Newserializers(instance=queryset,many=True) #ser.data 1.寻找get_serializer() 2. ''' 源码 def get_serializer(self, *args, **kwargs): serializer_class = self.get_serializer_class() kwargs['context'] = self.get_serializer_context() return serializer_class(*args, **kwargs) ''' 3.进行寻找get_serializer_class()本类没有去父类找 #serializer_class = self.get_serializer_class() 4. ''' def get_serializer_class(self): assert self.serializer_class is not None, ( "'%s' should either include a `serializer_class` attribute, " "or override the `get_serializer_class()` method." % self.__class__.__name__ ) return self.serializer_class ''' #return self.serializer_class 返回serializer_class #get_serializer()=serializer_class 在本类定义seializer_class seializer_class=Newserializers 至关于 #ser=Newserializers(instance=queryset,many=True) #ser.data ser=self.get_serializer(instance=queryset,many=True) ser.data
分页
querset=model.News.objects.all()
pagination_class =PageNumberPagination
self.paginate_queryset(queryset)
'''
源码
self.paginate_queryset()
self._paginator = self.pagination_class()
须要定义
def paginator(self):
if not hasattr(self, '_paginator'):
if self.pagination_class is None:
self._paginator = None
else:
self._paginator = self.pagination_class()
return self._paginator
'''pagination_class()须要本地定义
pagination_class =PageNumberPagination
````
源码的剖析 def get_queryset(self): """ Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using `self.queryset`. This method should always be used rather than accessing `self.queryset` directly, as `self.queryset` gets evaluated only once, and those results are cached for all subsequent requests. You may want to override this if you need to provide different querysets depending on the incoming request. (Eg. return a list of items that is specific to the user) """ assert self.queryset is not None, ( "'%s' should either include a `queryset` attribute, " "or override the `get_queryset()` method." % self.__class__.__name__ ) queryset = self.queryset if isinstance(queryset, QuerySet): # Ensure queryset is re-evaluated on each request. queryset = queryset.all() return queryset def get_object(self): """ Returns the object the view is displaying. You may want to override this if you need to provide non-standard queryset lookups. Eg if objects are referenced using multiple keyword arguments in the url conf. """ queryset = self.filter_queryset(self.get_queryset()) # Perform the lookup filtering. lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field assert lookup_url_kwarg in self.kwargs, ( 'Expected view %s to be called with a URL keyword argument ' 'named "%s". Fix your URL conf, or set the `.lookup_field` ' 'attribute on the view correctly.' % (self.__class__.__name__, lookup_url_kwarg) ) filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]} obj = get_object_or_404(queryset, **filter_kwargs) # May raise a permission denied self.check_object_permissions(self.request, obj) return obj def get_serializer(self, *args, **kwargs): """ Return the serializer instance that should be used for validating and deserializing input, and for serializing output. """ serializer_class = self.get_serializer_class() kwargs['context'] = self.get_serializer_context() return serializer_class(*args, **kwargs) def get_serializer_class(self): """ Return the class to use for the serializer. Defaults to using `self.serializer_class`. You may want to override this if you need to provide different serializations depending on the incoming request. (Eg. admins get full serialization, others get basic serialization) """ assert self.serializer_class is not None, ( "'%s' should either include a `serializer_class` attribute, " "or override the `get_serializer_class()` method." % self.__class__.__name__ ) return self.serializer_class def get_serializer_context(self): """ Extra context provided to the serializer class. """ return { 'request': self.request, 'format': self.format_kwarg, 'view': self } def filter_queryset(self, queryset): """ Given a queryset, filter it with whichever filter backend is in use. You are unlikely to want to override this method, although you may need to call it either from a list view, or from a custom `get_object` method if you want to apply the configured filtering backend to the default queryset. """ for backend in list(self.filter_backends): queryset = backend().filter_queryset(self.request, queryset, self) return queryset
from rest_framework.generics import GenericAPIView from rest_framework.filters import BaseFilterBackend from rest_framework import serializers GenericAPIview继承了APIview class Newserializers(serializers.ModelSerializer): class Meta: model=models.News fields="__all__" class NewFiltrBackend(BaseFilterBackend): def filter_queryset(self,request,queryset,view): val = request.query_params.get('cagetory') return queryset.filter(category_id=val) class NewView(GenericAPIview): querset=model.News.objects.all() filter_backends=[NewFiltrBackend ,] seializer_class=Newserializers def get(self,request,*arg,**kwargs): #self对应NewView的对象 v=self.get_queryset() queryset=self.filter_queryset(v) self.get_serializer(instance==queryset,many=True) retutn Response(ser.data) ''' 1.querset=model.News.objects.all() 2.filter_backends=[NewFiltrBackend ,] 3.seializer_class=Newserializers 4.pagination_class =PageNumberPagination 1.查询 self.get_queryset()#等于querset=model.News.objects.all() 2.序列化 self.get_serializer()等于seializer_class=Newserializers() 3.筛选 self.filter_queryset() 等于filter_backends=[NewFiltrBackend ,] 内部有一个for循环列表 同等与NewFiltrBackend() 4.分页 self.paginate_queryset(queryset) 等于page_obj=PageNumberPagination() '''
基于GenericAPIView
基于GenericAPIView
1.queryset 2.分页 #settings的配置 paginator.paginate_queryset(queryset, self.request, view=self) "DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination", "PAGE_SIZE":2, 3.序列化 4.返回序列化数据
#源码 def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) 1.执行list def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) #self.get_queryset()执行自定的 #queryset=model.News.objects.all() #self.filter_queryset()筛选执行 #filter_backends=[NewFiltrBackend ,] #执行对应本类的filter_queryset 2. page = self.paginate_queryset(queryset) #分页 #执行 pagination_class =PageNumberPagination if page is not None: 3.serializer = self.get_serializer(page, many=True) #序列化 执行seializer_class=Newserializers return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) #最后返回序列化的数据
模拟get请求
1. #对应得到对象 本身要定义单条数据 queryset 2. #序列化 3. #返回数据
class RetrieveAPIView(mixins.RetrieveModelMixin, GenericAPIView): #从左往右继承 def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) #去找retrieve这个方法 class RetrieveModelMixin: """ Retrieve a model instance. """ def retrieve(self, request, *args, **kwargs): #对应得到对象 instance = self.get_object() #序列化 serializer = self.get_serializer(instance) #返回数据 return Response(serializer.data) 执行 def get_object(self): """ Returns the object the view is displaying. You may want to override this if you need to provide non-standard queryset lookups. Eg if objects are referenced using multiple keyword arguments in the url conf. """ queryset = self.filter_queryset(self.get_queryset()) # Perform the lookup filtering. lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field assert lookup_url_kwarg in self.kwargs, ( 'Expected view %s to be called with a URL keyword argument ' 'named "%s". Fix your URL conf, or set the `.lookup_field` ' 'attribute on the view correctly.' % (self.__class__.__name__, lookup_url_kwarg) ) return obj
基于GenericAPIView
基于GenericAPIView
封装了post请求
1.序列化 2.序列化验证 3.保存数据
def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) 1.执行create def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) #序列化 serializer.is_valid(raise_exception=True) #序列化验证 self.perform_create(serializer) ''' 保存数据 def perform_create(self, serializer): serializer.save() ''' headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
#第一种写法重写create书写 多条序列化器 def create(self, request, *args, **kwargs): article = articleseralizer(data=request.data) articleDetail = atricleDetaliser(data=request.data) if article.is_valid() and articleDetail.is_valid(): article_obj = article.save(author=request.user) print(">>>>>>>>>", request.user, type(request.user)) articleDetail.save(article=article_obj) return Response('修改为功') else: return Response(f"{article.errors},{articleDetail.errors}") #第二种方式 重写序列化器分发 def get_serializer_class(self): self.request.method=="POST": return 对应序列化器 self.request.method=="GET": return 对应序列化器 def perform_create(self,serializer): Aritlel=serializer.save(author) serializer_second=AricleSerializer(data=request.data) serializer.save(aritle=Aritlel) 字段对象=对象
封装了局部更新和全局更新
基于GenericAPIView
1.获取更新那条数据的对象 2.序列化 2.序列化校验 3.校验成功更新
def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) ''' def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) instance = self.get_object() ''' #执行queryset查询处理的model对象 #至关于 #queryset=model.表名.object.filter(pk=pk).frist() ''' serializer = self.get_serializer(instance, data=request.data, partial=partial)默认不为Ture #执行对应的序列化 serializer.is_valid(raise_exception=True) #序列化验证 self.perform_update(serializer) ''' # def perform_update(self, serializer): #serializer.save() def patch(self, request, *args, **kwargs): return self.partial_update(request, *args, **kwargs) ''' def partial_update(self, request, *args, **kwargs): kwargs['partial'] = True return self.update(request, *args, **kwargs) ''' 执行 ''' def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) instance = self.get_object() ''' #执行queryset查询处理的model对象 #至关于 #queryset=model.表名.object.filter(pk=pk).frist() ''' serializer = self.get_serializer(instance, data=request.data, partial=partial) kwargs['partial'] = True 局部更新 #执行对应的序列化 serializer.is_valid(raise_exception=True) #序列化验证 self.perform_update(serializer) ''' # def perform_update(self, serializer): #serializer.save()
1.传入对象 2.执行def perform_destroy(self, instance): 3.删除
def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs) def destroy(self, request, *args, **kwargs): instance = self.get_object() #c传入对象删除 self.perform_destroy(instance) return Response(status=status.HTTP_204_NO_CONTENT) def perform_destroy(self, instance): instance.delete()#删除
是APIview视图类的继承对象
createapiview UpdateAPIview。。。。等等
1.直接继承便可 2.由于没有封装对应get post等请求方法 一点点去找父类的方法 3.GenericViewSet(ViewSetMixin)它继承的ViewSetMixin 4.重写了asview()须要传参 5.使用两个类进行id和没有id的区分 url(r'^article/$',article.AtricleView.as_view({"get":"list"})) url(r'^article/(?P<pk>\d+)/$',article.AtricleView.as_view({"get":"retrieve"}))
from rest_framework.viewsets import GenericVIewSet class GenericViewSet(ViewSetMixin,generics.GenericAPIView ): 默认继承GenericAPIView def get_serializer_class(self): pk = self.kwargs.get('pk') if pk: return ArticleDetailSerializer return ArticleSerializer
def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data)
def retrieve(self, request, *args, **kwargs): instance = self.get_object() serializer = self.get_serializer(instance) return Response(serializer.data)
def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) instance = self.get_object() serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) if getattr(instance, '_prefetched_objects_cache', None): # If 'prefetch_related' has been applied to a queryset, we need to # forcibly invalidate the prefetch cache on the instance. instance._prefetched_objects_cache = {} return Response(serializer.data) def perform_update(self, serializer): serializer.save() # def partial_update(self, request, *args, **kwargs): kwargs['partial'] = True return self.update(request, *args, **kwargs) class
删除
class DestroyModelMixin: """ Destroy a model instance. """ def destroy(self, request, *args, **kwargs): instance = self.get_object() self.perform_destroy(instance) return Response(status=status.HTTP_204_NO_CONTENT) def perform_destroy(self, instance): instance.delete()
setting配置
default_version = api_settings.DEFAULT_VERSION #默认版本 allowed_versions = api_settings.ALLOWED_VERSIONS #指定版本 version_param = api_settings.VERSION_PARAM #url版本传输关键字 #指定版本类类 "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
dispath 老的request 封装了不少功能 request.version#版本 scheme#版本对象 #执行返回一个版本元组 第一个是版本第二个是版本对象 def determine_version(self, request, *args, **kwargs): """ If versioning is being used, then determine any API version for the incoming request. Returns a two-tuple of (version, versioning_scheme) """ if self.versioning_class is None: return (None, None) scheme = self.versioning_class() return (scheme.determine_version(request, *args, **kwargs), scheme) request.version, request.versioning_scheme = version, scheme
class APIView(View): versioning_class = api_settings.DEFAULT_VERSIONING_CLASS def dispatch(self, request, *args, **kwargs): # ###################### 第一步 ########################### """ request,是django的request,它的内部有:request.GET/request.POST/request.method args,kwargs是在路由中匹配到的参数,如: url(r'^order/(\d+)/(?P<version>\w+)/$', views.OrderView.as_view()), http://www.xxx.com/order/1/v2/ """ self.args = args self.kwargs = kwargs """ request = 生成了一个新的request对象,此对象的内部封装了一些值。 request = Request(request) - 内部封装了 _request = 老的request """ request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: # ###################### 第二步 ########################### self.initial(request, *args, **kwargs) 执行视图函数。。 def initial(self, request, *args, **kwargs): # ############### 2.1 处理drf的版本 ############## version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme ... def determine_version(self, request, *args, **kwargs): if self.versioning_class is None: return (None, None) #是版本类的实例化对象 scheme = self.versioning_class() # obj = XXXXXXXXXXXX() return (scheme.determine_version(request, *args, **kwargs), scheme) class OrderView(APIView): versioning_class = URLPathVersioning def get(self,request,*args,**kwargs): print(request.version) print(request.versioning_scheme) return Response('...') def post(self,request,*args,**kwargs): return Response('post')
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.request import Request from rest_framework.versioning import URLPathVersioning class OrderView(APIView): versioning_class = URLPathVersioning def get(self,request,*args,**kwargs): print(request.version) print(request.versioning_scheme) return Response('...') def post(self,request,*args,**kwargs): return Response('post')
REST_FRAMEWORK = { "ALLOWED_VERSIONS":['v1','v2'], 'VERSION_PARAM':'version' }
不用手动一个个加所有都配置上
url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'), url(r'^(?P<version>\w+)/users/$', users_list, name='users-list'),
settings配置
REST_FRAMEWORK = { 指定版本类 必需要写要些路径 "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning", #限制版本范围 "ALLOWED_VERSIONS":['v1','v2'], #指定url有名分组的关键字 'VERSION_PARAM':'version' }
自定义认证token 把数据库的user拿出来比较 若是想使用**request.data 就要把他变为字典 因此前端要返回json数据类型 源码写法 #配置 authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
和登录配合使用 import uuid from django.shortcuts import render from django.views import View from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator from rest_framework.versioning import URLPathVersioning from rest_framework.views import APIView from rest_framework.response import Response class Loginview(APIView): versioning_class = None #认证写法 authentication_classes = [Myauthentication, ] def post(self,request,*args,**kwargs): user_object = models.UserInfo.objects.filter(**request.data).first() if not user_object: return Response('登陆失败') random_string = str(uuid.uuid4()) user_object.token = random_string user_object.save() return Response(random_string) class MyAuthentication: def authenticate(self, request): """ Authenticate the request and return a two-tuple of (user, token). """ token = request.query_params.get('token') user_object = models.UserInfo.objects.filter(token=token).first() if user_object: return (user_object,token) return (None,None) class OrderView(APIView): authentication_classes = [MyAuthentication, ] def get(self,request,*args,**kwargs): print(request.user) print(request.auth) return Response('order') class UserView(APIView): authentication_classes = [MyAuthentication,] def get(self,request,*args,**kwargs): print(request.user) print(request.auth) return Response('user') ···············手写认证类···························· class Myauthentication: # 认证 # versioning_class = None #手写认证 def authenticate(self, request): token = request.query_params.get('token') print(token) user_object = models.UserInfo.objects.filter(token=token).first() print(user_object) if user_object: print(1) #必须返回一个元组的形式 #返回对应的user_object和token值 return (user_object, token) else: print(2) return (None, None) ---------------------- 为何要返回元组 ---------------------- #为何要返回元组 def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ for authenticator in self.authenticators: try: user_auth_tuple = #到这返回为何能够执行对应的方法 authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() raise if user_auth_tuple is not None: #把查询出来的元组 self._authenticator = authenticator #给user和auth进行赋值 self.user, self.auth = user_auth_tuple#必须返回一个元组的形式 return#结束函数 self._not_authenticated() def _not_authenticated(self): """ Set authenticator, user & authtoken representing an unauthenticated request. Defaults are None, AnonymousUser & None. """ self._authenticator = None if api_settings.UNAUTHENTICATED_USER: #user 没有设置返回none self.user = #匿名用户 api_settings.UNAUTHENTICATED_USER() else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = #对应author没有设置返回none api_settings.UNAUTHENTICATED_TOKEN() else: self.auth = None
DEFAULT_AUTHENTICATION_CLASSES=['写一个认证的类手写的'] class Myauthentication: # 认证 # versioning_class = None #手写认证 def authenticate(self, request): token = request.query_params.get('token') print(token) user_object = models.UserInfo.objects.filter(token=token).first() print(user_object) if user_object: print(1) #必须返回一个元组的形式 #返回对应的user_object和token值 return (user_object, token) else: print(2) return (None, None) ''' # raise Exception(), 不在继续往下执行,直接返回给用户。 # return None ,本次认证完成,执行下一个认证 # return ('x',"x"),认证成功,不须要再继续执行其余认证了,继续日后权限、节流、视图函数 '''
class LoginView(APIView): authentication_classes = [] def post(self,request,*args,**kwargs): user_object = models.UserInfo.objects.filter(**request.data).first() if not user_object: return Response('登陆失败') random_string = str(uuid.uuid4()) user_object.token = random_string user_object.save() return Response(random_string) class OrderView(APIView): # authentication_classes = [TokenAuthentication, ] def get(self,request,*args,**kwargs): print(request.user) print(request.auth) if request.user: return Response('order') return Response('滚') class UserView(APIView): 同上
''' 当请求发过来的实话会先执行 apiview里面的dispath方法 1.执行认证的dispatch的方法 2.dispatch方法会执行initialize_request方法封装旧的request对象 3.在封装的过程当中会执行 get_authenticators(self): 并实例化列表中的每个认证类 若是本身写的类有对应的认证类 会把认证类的实例化对象封装到新的request当中 继续执行initial 会执行认证里面的perform_authentication 执行request.user 会执行循环每个对象执行对应的authenticate方法 会有三种返回值 # raise Exception(), 不在继续往下执行,直接返回给用户。 # return None ,本次认证完成,执行下一个认证 # return ('x',"x"),认证成功,不须要再继续执行其余认证了,继续日后权限、节流、视图函数 里面会执行四件事 版本 认证 权限 节流 ''' def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs #封装旧的request request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? 循环自定义的认证 #request封装旧的request执行initialize_request def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ parser_context = self.get_parser_context(request) return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context ) def get_authenticators(self): """ Instantiates and returns the list of authenticators that this view can use. """ return [auth() for auth in self.authentication_classes] def initial(self, request, *args, **kwargs): """ Runs anything that needs to occur prior to calling the method handler. """ self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request neg = self.perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use. version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request) #执行完四件事在执行视图
message=消息 自定义错误消息 能够自定义错误信息 ''' for permission in self.get_permissions(): if not permission.has_permission(request, self): self.permission_denied( request, message=getattr(permission, 'message', None) ) ''' 本质上自定义和 return Flase 单条验证 验证两次 不写url_kwarg默认封装识别pk关键字
message={"code":"恭喜你报错了"}
from rest_framework.permissions import BasePermission from rest_framework import exceptions class MyPermission(BasePermission): #第一种 message = {'code': 10001, 'error': '你没权限'} def has_permission(self, request, view): #request.user执行认证 if request.user: return True #自定义错误信息 raise exceptions.PermissionDenied({'code': 10001, 'error': '你没权限'}) return False def has_object_permission(self, request, view, obj): """ Return `True` if permission is granted, `False` otherwise. """ return False #对应源码 至关于重写了错误信息 #执行顺序 dispatch---》intital--》check_permissions--》permission_denied def permission_denied(self, request, message=None): """ If request is not permitted, determine what kind of exception to raise. """ if request.authenticators and not request.successful_authenticator: raise exceptions.NotAuthenticated() raise #等于message 或者自定义错误函数 #raise exceptions.PermissionDenied({'code': 10001, 'error': '你没权限'}) exceptions.PermissionDenied(detail=message) ················源码·················· def check_permissions(self, request): for permission in self.get_permissions(): if not permission.has_permission(request, self): self.permission_denied( request, message=getattr(permission, 'message', None) )
apiview --- dispatch --- initial --- 执行权限判断 若是本身写了权限类 会先循环权限类并实例化存在列表当中 而后循环列表对象,若是权限判断返回不为Ture机会进行主动抛出异常 能够自定义错误信息
class APIView(View): permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES def dispatch(self, request, *args, **kwargs): 封装request对象 self.initial(request, *args, **kwargs) 经过反射执行视图中的方法 def initial(self, request, *args, **kwargs): 版本的处理 # 认证 self.perform_authentication(request) # 权限判断 self.check_permissions(request) self.check_throttles(request) #执行认证 def perform_authentication(self, request): request.user def check_permissions(self, request): # [对象,对象,] self.执行 ''' def get_permissions(self): return [permission() for permission in self.permission_classes] ''' for permission in self.get_permissions(): if not permission.has_permission(request, self): self.permission_denied(request, message=getattr(permission, 'message', None)) def permission_denied(self, request, message=None): if request.authenticators and not request.successful_authenticator: raise exceptions.NotAuthenticated() raise exceptions.PermissionDenied(detail=message) # class UserView(APIView): permission_classes = [MyPermission, ] def get(self,request,*args**kwargs): return Response('user')
class MyPermission(BasePermission): message = {'code': 10001, 'error': '你没权限'} def has_permission(self, request, view): #request.user执行认证 if request.user: return True raise exceptions.PermissionDenied({'code': 10001, 'error': '你没权限'}) return False def has_object_permission(self, request, view, obj): """ Return `True` if permission is granted, `False` otherwise. """ return False class UserView(APIView): 先执行上面的数据 permission_classes = [MyPermission, ] #再执行 def get(self,request,*args**kwargs): return Response('user')
``python def dispatch(self, request, *args, **kwargs): """
.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try: #走这 self.initial(request, *args, **kwargs)
`````````````````def initial``````````````````````````
def initial(self, request, *args, **kwargs): """ Runs anything that needs to occur prior to calling the method handler. """ self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request neg = self.perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use. version, scheme = ''' 第三步 ''' self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request)
def check_permissions(self, request):
# [对象,对象,]
self.执行
'''
def get_permissions(self):
return [permission() for permission in self.permission_classes]
'''
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(request, message=getattr(permission, 'message', None))
def permission_denied(self, request, message=None):
if request.authenticators and not request.successful_authenticator:
raise exceptions.NotAuthenticated()
raise exceptions.PermissionDenied(detail=message)
```
def determine_version(self, request, *args, **kwargs): if self.versioning_class is None: return (None, None) scheme = self.versioning_class() return (scheme.determine_version(request, *args, **kwargs), scheme)
单挑数据
def has_object_permission(self, request, view, obj): """ Return `True` if permission is granted, `False` otherwise. """ return False #在执行RetrieveAPIView 会执行单挑数据的验证 def get_object(self): queryset = self.filter_queryset(self.get_queryset()) # Perform the lookup filtering. #不写url_kwarg默认封装识别pk关键字 lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field assert lookup_url_kwarg in self.kwargs, ( 'Expected view %s to be called with a URL keyword argument ' 'named "%s". Fix your URL conf, or set the `.lookup_field` ' 'attribute on the view correctly.' % (self.__class__.__name__, lookup_url_kwarg) ) filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]} obj = get_object_or_404(queryset, **filter_kwargs) # May raise a permission denied #检验单挑数据的权限 self.check_object_permissions(self.request, obj) return obj ···················check_object_permissions·········· def check_object_permissions(self, request, obj): """ Check if the request should be permitted for a given object. Raises an appropriate exception if the request is not permitted. """ for permission in self.get_permissions(): if not permission.has_object_permission(request, self, obj): self.permission_denied( request, message=getattr(permission, 'message', None) ) 执行has_object_permission def has_object_permission(self, request, view, obj): return True
1.首先全部方法都执行父类的apiview方法 2.执行dispatch方法 3.进行 def get_authenticators(self): return [auth() for auth in self.authentication_classes] 4. intiail request.user
from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator def csrf_exempt(view_func): """ Marks a view function as being exempt from the CSRF view protection. """ # We could just do view_func.csrf_exempt = True, but decorators # are nicer if they don't have side-effects, so we return a new # function. def wrapped_view(*args, **kwargs): return view_func(*args, **kwargs) wrapped_view.csrf_exempt = True return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)
- 匿名用户,用IP做为用户惟一标记,但若是用户换代理IP,没法作到真正的限制。 - 登陆用户,用用户名或用户ID作标识。能够作到真正的限制
{ throttle_anon_1.1.1.1:[100121340,], 1.1.1.2:[100121251,100120450,] } 限制:60s能访问3次 来访问时: 1.获取当前时间 100121280 2.100121280-60 = 100121220,小于100121220全部记录删除所有剔除 3.判断1分钟之内已经访问多少次了? 4 判断访问次数 4.没法访问 停一会 来访问时: 1.获取当前时间 100121340 2.100121340-60 = 100121280,小于100121280全部记录删除 3.判断1分钟之内已经访问多少次了? 0 4.能够访问
在视图类中配置 throttle_classes= [] 这是一个列表 实现 allow_request 方法 wait 是一个提示方法 返回 True/False。 True 能够继续访问, False 表示限制 全局配置 DEFAULT_THROTTLE_CLASSE,节流限制类 DEFAULT_THROTTLE_RATES 表示频率限制,好比 10/m 表示 每分钟 10 次 源码在 initial 中实现 check_throttles 方法 在进行节流以前已经作了版本认证权限 #dispatch分发 def initial(self, request, *args, **kwargs): #版本 version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted #认证 self.perform_authentication(request) #权限 self.check_permissions(request) #频率限制 self.check_throttles(request) 1.先找allow_request本类没有去父类找 同dispatch 2.执行对应的rate获取对应scope 节流的频率 2.key是获取ip值 3.history是获取ip地址 获取对应的访问时间记录 ''' self.history = self.cache.get(self.key, []) ''' 4.对应的访问记录等于ture获取到值, 把当前的时间减去一个本身设定的时间戳 ''' while self.history and self.history[-1] <= self.now - self.duration: #条件知足 把最后一个值删除 #再去循环判断 self.history.pop() ''' 5.而后判断访问次数有没有大于本身设定的值 ''' if len(self.history) >= self.num_requests: 知足条件走这 return self.throttle_failure() 不知足条件走这 return self.throttle_success() ''' 6.成功 def throttle_success(self): """ Inserts the current request's timestamp along with the key into the cache. """ self.history.insert(0, self.now) self.cache.set(self.key, self.history, self.duration) return True 6.失败 def throttle_failure(self): """ Called when a request to the API has failed due to throttling. """ return False#不能够访问 7若是返回false进入等待时间 def wait(self): """ Returns the recommended next request time in seconds. """ if self.history: remaining_duration = self.duration - (self.now - self.history[-1]) else: remaining_duration = self.duration available_requests = self.num_requests - len(self.history) + 1 if available_requests <= 0: return None return remaining_duration / float(available_requests)
#dispatch分发 def initial(self, request, *args, **kwargs): #版本 version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted #认证 self.perform_authentication(request) #权限 self.check_permissions(request) #频率限制 self.check_throttles(request) def check_throttles(self, request): throttle_durations = [] for throttle in self.get_throttles(): #找到当前类的allow_request if not throttle.allow_request(request, self): throttle_durations.append(throttle.wait()) if throttle_durations: durations = [ duration for duration in throttle_durations if duration is not None ] duration = max(durations, default=None) self.throttled(request, duration) #执行类的allow_request def allow_request(self, request, view): """ Implement the check to see if the request should be throttled. On success calls `throttle_success`. On failure calls `throttle_failure`. """ if self.rate is None: return True 获取 ''' def __init__(self): if not getattr(self, 'rate', None): self.rate = self.get_rate() self.num_requests, self.duration = self.parse_rate(self.rate) 当前这个类有一个获取rate的方法 ''' #执行get_rate 读取settings设置的配置文件 ''' def get_rate(self): #若是不设置会进行报错 try: #设置了就进行键取值 return self.THROTTLE_RATES[self.scope] except KeyError: msg = "No default throttle rate set for '%s' scope" % self.scope raise ImproperlyConfigured(msg) ''' #经过键去取值/进行分割获取值 ''' def parse_rate(self, rate): if rate is None: return (None, None) num, period = rate.split('/') num_requests = int(num) duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] return (num_requests, duration) ''' #这步继续往下执行继承类的get_cache_key self.key = self.get_cache_key(request, view) #匿名 #继承了class AnonRateThrottle(SimpleRateThrottle): ''' scope = 'anon' def get_cache_key(self, request, view): if request.user.is_authenticated: return None # Only throttle unauthenticated requests. return self.cache_format % { 'scope': self.scope, 'ident': self.get_ident(request) } } #返回字符串格式化 throttle_(匿名)anon_(ip地址的拼接)1.1.1.1:[100121340,], ''' #根据用户进行判断 重写get_cache_key ''' ''' if self.key is None: return True self.history = self.cache.get(self.key, []) self.now = self.timer() # Drop any requests from the history which have now passed the # throttle duration while self.history and self.history[-1] <= self.now - self.duration: self.history.pop() #元组返回的值num_requests if len(self.history) >= self.num_requests: #不能够访问 return self.throttle_failure() #能够访问 return self.throttle_success() ''' 6.成功 def throttle_success(self): #成功把访问的当前时间插入history self.history.insert(0, self.now) self.cache.set(self.key, self.history, self.duration) return True 6.失败 def throttle_failure(self): """ Called when a request to the API has failed due to throttling. """ return False#不能够访问 '''
REST_FRAMEWORK = { throttle_classes = [AnonRateThrottle,] "DEFAULT_THROTTLE_RATES":{"anon":"3/m"} #这样写的缘由 源码 #经过匿名 scope = 'anon' def get_cache_key(self, request, view): if request.user.is_authenticated: return None # Only throttle unauthenticated requests. return self.cache_format % { 'scope': self.scope, 'ident': self.get_ident(request) } } #获取全局设置的步骤 def __init__(self): if not getattr(self, 'rate', None): #第一步 self.rate = self.get_rate() ''' 以设置的键进行取值获取时间 def get_rate(self): 若是不设置就进行报错 try: return self.THROTTLE_RATES[self.scope] except KeyError: msg = "No default throttle rate set for '%s' scope" % self.scope raise ImproperlyConfigured(msg) ''' self.num_requests, self.duration = self.parse_rate(self.rate) #后面设置值格式 #例 "DEFAULT_THROTTLE_RATES":{"anon":"3/m"} 以/分割 前面是限制的次数 后面的是访问限制的时间 ''' def parse_rate(self, rate): if rate is None: return (None, None) num, period = rate.split('/') num_requests = int(num) duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] return (num_requests, duration) ''' 以后执行对应字典的取值 key=throttle_(匿名)anon_(ip地址的拼接)1.1.1.1: [100121340,],#值 self.key = self.get_cache_key(request, view) if self.key is None: return True self.history = self.cache.get(self.key, []) self.now = self.timer() # Drop any requests from the history which have now passed the # throttle duration while self.history and self.history[-1] <= self.now - self.duration: self.history.pop() if len(self.history) >= self.num_requests: return self.throttle_failure() return self.throttle_success()
REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': [ 'api.utils.throttles.throttles.LuffyAnonRateThrottle', 'api.utils.throttles.throttles.LuffyUserRateThrottle', ], 'DEFAULT_THROTTLE_RATES': { #不重写的默认走这 'anon': '10/day', 'user': '10/day', 'luffy_anon': '10/m', 'luffy_user': '20/m', }, } settings
from django.conf.urls import url, include from web.views.s3_throttling import TestView urlpatterns = [ url(r'^test/', TestView.as_view()), ] urls.py
REST_FRAMEWORK = { 'UNAUTHENTICATED_USER': None, 'UNAUTHENTICATED_TOKEN': None, 'DEFAULT_THROTTLE_RATES': { 'luffy_anon': '10/m', 'luffy_user': '20/m', }, } settings.py
根据匿名ip或者user 进行判断
#!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.throttling import SimpleRateThrottle class LuffyAnonRateThrottle(SimpleRateThrottle): """ 匿名用户,根据IP进行限制 """ scope = "luffy_anon" def get_cache_key(self, request, view): # 用户已登陆,则跳过 匿名频率限制 if request.user: return None return self.cache_format % { 'scope': self.scope, 'ident': self.get_ident(request) } class LuffyUserRateThrottle(SimpleRateThrottle): """ 登陆用户,根据用户token限制 """ #重写scope scope = "luffy_user" def get_ident(self, request): """ 认证成功时:request.user是用户对象;request.auth是token对象 :param request: :return: """ # return request.auth.token return "user_token" def get_cache_key(self, request, view): """ 获取缓存key :param request: :param view: :return: """ # 未登陆用户,则跳过 Token限制 if not request.user: return None return self.cache_format % { 'scope': self.scope, 'ident': self.get_ident(request) } class TestView(APIView): throttle_classes = [LuffyUserRateThrottle, LuffyAnonRateThrottle, ] def get(self, request, *args, **kwargs): # self.dispatch print(request.user) print(request.auth) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs): return Response('POST请求,响应内容') def put(self, request, *args, **kwargs): return Response('PUT请求,响应内容')
class AnonRateThrottle(SimpleRateThrottle):
scope = 'anon'
def get_cache_key(self, request, view):
if request.user.is_authenticated:
return None
# Only throttle unauthenticated requests.
return self.cache_format % { 'scope': self.scope, 'ident': self.get_ident(request) }
def get_cache_key(self, request, view):
return self.get_ident(request)#获取对应的ip值
````
# settings.py 'DEFAULT_THROTTLE_RATES': { 'Vistor': '3/m', 'User': '10/m' },
from django.views.decorators.csrf import csrf_exempt from django.shortcuts import HttpResponse @csrf_exempt def index(request): return HttpResponse('...') # index = csrf_exempt(index) urlpatterns = [ url(r'^index/$',index), ]
urlpatterns = [ url(r'^login/$',account.LoginView.as_view()), ] class APIView(View): @classmethod def as_view(cls, **initkwargs): view = super().as_view(**initkwargs) view.cls = cls view.initkwargs = initkwargs # Note: session based authentication is explicitly CSRF validated, # all other authentication is CSRF exempt. return csrf_exempt(view)
第一种:原始APIView
url(r'^login/$',account.LoginView.as_view()),
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework_jwt.settings import api_settings from rest_framework.throttling import AnonRateThrottle from api import models class LoginView(APIView): authentication_classes = [] def post(self,request,*args,**kwargs): # 1.根据用户名和密码检测用户是否能够登陆 user = models.UserInfo.objects.filter(username=request.data.get('username'),password=request.data.get('password')).first() if not user: return Response({'code':10001,'error':'用户名或密码错误'}) # 2. 根据user对象生成payload(中间值的数据) jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER payload = jwt_payload_handler(user) # 3. 构造前面数据,base64加密;中间数据base64加密;前两段拼接而后作hs256加密(加盐),再作base64加密。生成token jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER token = jwt_encode_handler(payload) return Response({'code': 10000, 'data': token})
第二种:ListApiView等
url(r'^article/$',article.ArticleView.as_view()), url(r'^article/(?P<pk>\d+)/$',article.ArticleDetailView.as_view()),
from rest_framework.throttling import AnonRateThrottle from rest_framework.response import Response from rest_framework.generics import ListAPIView,RetrieveAPIView from api import models from api.serializer.article import ArticleSerializer,ArticleDetailSerializer class ArticleView(ListAPIView): authentication_classes = [] # throttle_classes = [AnonRateThrottle,] queryset = models.Article.objects.all() serializer_class = ArticleSerializer class ArticleDetailView(RetrieveAPIView): authentication_classes = [] queryset = models.Article.objects.all() serializer_class = ArticleDetailSerializer
第三种:
url(r'^article/$',article.ArticleView.as_view({"get":'list','post':'create'})), url(r'^article/(?P<pk>\d+)/$',article.ArticleView.as_view({'get':'retrieve','put':'update','patch':'partial_update','delete':'destroy'}))
from rest_framework.viewsets import GenericViewSet from rest_framework.mixins import ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin from api.serializer.article import ArticleSerializer,ArticleDetailSerializer class ArticleView(GenericViewSet,ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin): authentication_classes = [] throttle_classes = [AnonRateThrottle,] queryset = models.Article.objects.all() serializer_class = None def get_serializer_class(self): pk = self.kwargs.get('pk') if pk: return ArticleDetailSerializer return ArticleSerializer
装饰器
def outer(func): def inner(*args,**kwargs): return func(*args,**kwargs) return inner @outer def index(a1): pass index()
def outer(func): def inner(*args,**kwargs): return func(*args,**kwargs) return inner def index(a1): pass index = outer(index) index()
django中能够免除csrftoken认证
from django.views.decorators.csrf import csrf_exempt from django.shortcuts import HttpResponse @csrf_exempt def index(request): return HttpResponse('...') # index = csrf_exempt(index) urlpatterns = [ url(r'^index/$',index), ]
urlpatterns = [ url(r'^login/$',account.LoginView.as_view()), ] class APIView(View): @classmethod def as_view(cls, **initkwargs): view = super().as_view(**initkwargs) view.cls = cls view.initkwargs = initkwargs # Note: session based authentication is explicitly CSRF validated, # all other authentication is CSRF exempt. return csrf_exempt(view)
面向对象中基于继承+异常处理来作的约束
class BaseVersioning: def determine_version(self, request, *args, **kwargs): raise NotImplementedError("must be implemented") class URLPathVersioning(BaseVersioning): def determine_version(self, request, *args, **kwargs): version = kwargs.get(self.version_param, self.default_version) if version is None: version = self.default_version if not self.is_allowed_version(version): raise exceptions.NotFound(self.invalid_version_message) return version
面向对象封装
class Foo(object): def __init__(self,name,age): self.name = name self.age = age obj = Foo('汪洋',18)
class APIView(View): def dispatch(self, request, *args, **kwargs): self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.request = request ... def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ parser_context = self.get_parser_context(request) return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), # [MyAuthentication(),] negotiator=self.get_content_negotiator(), parser_context=parser_context )
面向对象继承
class View(object): pass class APIView(View): def dispatch(self): method = getattr(self,'get') method() class GenericAPIView(APIView): serilizer_class = None def get_seriliser_class(self): return self.serilizer_class class ListModelMixin(object): def get(self): ser_class = self.get_seriliser_class() print(ser_class) class ListAPIView(ListModelMixin,GenericAPIView): pass class UserInfoView(ListAPIView): pass view = UserInfoView() view.dispatch()
class View(object): pass class APIView(View): def dispatch(self): method = getattr(self,'get') method() class GenericAPIView(APIView): serilizer_class = None def get_seriliser_class(self): return self.serilizer_class class ListModelMixin(object): def get(self): ser_class = self.get_seriliser_class() print(ser_class) class ListAPIView(ListModelMixin,GenericAPIView): pass class UserInfoView(ListAPIView): serilizer_class = "汪洋" view = UserInfoView() view.dispatch()
class View(object): pass class APIView(View): def dispatch(self): method = getattr(self,'get') method() class GenericAPIView(APIView): serilizer_class = None def get_seriliser_class(self): return self.serilizer_class class ListModelMixin(object): def get(self): ser_class = self.get_seriliser_class() print(ser_class) class ListAPIView(ListModelMixin,GenericAPIView): pass class UserInfoView(ListAPIView): def get_seriliser_class(self): return "咩咩" view = UserInfoView() view.dispatch()
反射
class View(object): def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs)
发送ajax请求
$.ajax({ url:'地址', type:'GET', data:{...}, success:function(arg){ console.log(arg); } })
浏览器具备 "同源策略的限制",致使 发送ajax请求
+ 跨域
存在没法获取数据。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>常鑫的网站</h1> <p> <input type="button" value="点我" onclick="sendMsg()"> </p> <p> <input type="button" value="点他" onclick="sendRemoteMsg()"> </p> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <script> function sendMsg() { $.ajax({ url:'/msg/', type:'GET', success:function (arg) { console.log(arg); } }) } function sendRemoteMsg() { $.ajax({ url:'http://127.0.0.1:8002/json/', type:'GET', success:function (arg) { console.log(arg); } }) } </script> </body> </html>
如何解决ajax+跨域?
CORS,跨站资源共享,本质:设置响应头。
常见的Http请求方法
get post put patch delete options
http请求中Content-type请起头
状况一: content-type:x-www-form-urlencode name=alex&age=19&xx=10 request.POST和request.body中均有值。 状况二: content-type:application/json {"name":"ALex","Age":19} request.POST没值 request.body有值。
django中F查询
django中获取空Queryset
models.User.object.all().none()
基于django的fbv和cbv都能实现遵循restful规范的接口
def user(request): if request.metho == 'GET': pass class UserView(View): def get()... def post...
基于django rest framework框架实现restful api的开发。
- 免除csrf认证 - 视图(APIView、ListAPIView、ListModelMinx) - 版本 - 认证 - 权限 - 节流 - 解析器 - 筛选器 - 分页 - 序列化 - 渲染器
简述drf中认证流程?
1.用户发来请求优先执行dispatch方法 2.内部会封装reqeustd1
简述drf中节流的实现原理以及过程?匿名用户/非匿名用户 如何实现频率限制?
GenericAPIView视图类的做用?
他提供了一些规则,例如: class GenericAPIView(APIView): serializer_class = None queryset = None lookup_field = 'pk' filter_backends = api_settings.DEFAULT_FILTER_BACKENDS pagination_class = api_settings.DEFAULT_PAGINATION_CLASS def get_queryset(self): return self.queryset def get_serializer_class(self): return self.serializer_class def filter_queryset(self, queryset): for backend in list(self.filter_backends): queryset = backend().filter_queryset(self.request, queryset, self) return queryset @property def paginator(self): if not hasattr(self, '_paginator'): if self.pagination_class is None: self._paginator = None else: self._paginator = self.pagination_class() return self._paginator 他至关于提供了一些规则,建议子类中使用固定的方式获取数据,例如: class ArticleView(GenericAPIView): queryset = models.User.objects.all() def get(self,request,*args,**kwargs): query = self.get_queryset() 咱们能够本身继承GenericAPIView来实现具体操做,可是通常不会,由于更加麻烦。 而GenericAPIView主要是提供给drf内部的 ListAPIView、Create.... class ListModelMixin: def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) class ListAPIView(mixins.ListModelMixin,GenericAPIView): def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) class MyView(ListAPIView): queryset = xxxx ser...
总结:GenericAPIView主要为drf内部帮助咱们提供增删改查的类LIstAPIView、CreateAPIView、UpdateAPIView、提供了执行流程和功能,咱们在使用drf内置类作CURD时,就能够经过自定义 静态字段(类变量)或重写方法(get_queryset、get_serializer_class)来进行更高级的定制。
jwt以及其优点。
jwt先后端分离 用于用户认证 jwt的实现原理: -用户登录成功,会给前端返回一个tokon值。 此token值只在前端保存 token值分为 一段类型和算法信息 第二段用户信息和超时时间 第三段前两段数据拼接以后进行has256再次加密+base64url
序列化时many=True和many=False的区别?
应用DRF中的功能进行项目开发
***** 解析器:request.query_parmas/request.data 视图 序列化 渲染器:Response **** request对象封装 版本处理 分页处理 *** 认证 权限 节流
因为浏览器具备同源策略的限制 对ajax请求的限制 同源同端口 不一样源就是跨域 写了/api 不跨域 api访问django api.xx.com跨域了访问django
发一次请求
设置响应头就能够解决 from django.shortcuts import render,HttpResponse def json(request): response = HttpResponse("JSONasdfasdf") response['Access-Control-Allow-Origin'] = "*" return response
预检option
请求
写法
from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator @csrf_exempt def put_json(request): response = HttpResponse("JSON复杂请求") if request.method == 'OPTIONS': # 处理预检 response['Access-Control-Allow-Origin'] = "*" response['Access-Control-Allow-Methods'] = "PUT" return response elif request.method == "PUT": return response
本质在数据返回值设置响应头 from django.shortcuts import render,HttpResponse def json(request): response = HttpResponse("JSONasdfasdf") response['Access-Control-Allow-Origin'] = "*" return response
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <p> <input type="button" onclick="Jsonp1();" value='提交'/> </p> <p> <input type="button" onclick="Jsonp2();" value='提交'/> </p> <script type="text/javascript" src="jquery-1.12.4.js"></script> <script> function Jsonp1(){ var tag = document.createElement('script'); tag.src = "http://c2.com:8000/test/"; document.head.appendChild(tag); document.head.removeChild(tag); } function Jsonp2(){ $.ajax({ url: "http://c2.com:8000/test/", type: 'GET', dataType: 'JSONP', success: function(data, statusText, xmlHttpRequest){ console.log(data); } }) } </script> </body> </html>
条件: 一、请求方式:HEAD、GET、POST 二、请求头信息: Accept Accept-Language Content-Language Last-Event-ID Content-Type 对应的值是如下三个中的任意一个 application/x-www-form-urlencoded multipart/form-data text/plain 注意:同时知足以上两个条件时,则是简单请求,不然为复杂请求
部署 collectstaic 收集静态文件
用于在先后端分离时,实现用户登陆相关。
用户登陆成功以后,生成一个随机字符串,给前端。 - 生成随机字符串 加密信息 #{typ:"jwt","alg":'HS256'} # 加密手段segments.append(base64url_encode(json_header)) 98qow39df0lj980945lkdjflo. #第二部分的信息 {id:1,username:'alx','exp':10} #加密手段segments.append(base64url_encode(payload)) saueoja8979284sdfsdf. #两个密文拼接加盐 asiuokjd978928374 - 类型信息经过base64加密 - 数据经过base64加密 - 两个密文拼接在h256加密+加盐 - 给前端返回token值只在前端 token是由。分割的三段组成 - 第一段:类型和算法信息 - 第二段。 用户的信息和超时时间 - 第三段:hs256(前两段拼接)加密 + base64url - 之后前端再次发来信息时 - 超时验证 - token合法性校验 前端获取随机字符串以后,保留起来。 之后再来发送请求时,携带98qow39df0lj980945lkdjflo.saueoja8979284sdfsdf.asiuokjd978928375。 后端接收到以后 1.取出第二部分进行时间的判断 2. 把前面两个进行加密对第三个值进行加密 - token只在前端保存,后端只负责校验。 - 内部集成了超时时间,后端能够根据时间进行校验是否超时。 - 因为内部存在hash256加密,因此用户不能够修改token,只要一修改就认证失败。 通常在先后端分离时,用于作用户认证(登陆)使用的技术。 jwt的实现原理: - 用户登陆成功以后,会给前端返回一段token。 - token是由.分割的三段组成。 - 第一段:类型和算法信心 - 第二段:用户信息+超时时间 - 第三段:hs256(前两段拼接)加密 + base64url - 之后前端再次发来信息时 - 超时验证 - token合法性校验 优点: - token只在前端保存,后端只负责校验。 - 内部集成了超时时间,后端能够根据时间进行校验是否超时。 - 因为内部存在hash256加密,因此用户不能够修改token,只要一修改就认证失败。
用户登录成功以后,生成一个随机字符串,本身保留一份,给前端返回一份。 之后前端再来发请求时,须要携带字符串 后端对字符串进行校验
pip3 install djangorestframework-jwt
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'api.apps.ApiConfig', 'rest_framework', 'rest_framework_jwt']
from rest_framework_jwt.views import obtain_jwt_token
用户信息加密 jwt_payload_hander = api_settings.JWT_PAYLOAD_HANDLER 第三段信息的加密 jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER #解密 jwt_decode_handler = api_settings.JWT_DECODE_HANDLER def jwt_payload_handler(user): username_field = get_username_field() username = get_username(user) warnings.warn( 'The following fields will be removed in the future: ' '`email` and `user_id`. ', DeprecationWarning ) payload = { 'user_id': user.pk, 'username': username, 'exp': datetime.utcnow() + #默认5分钟 api_settings.JWT_EXPIRATION_DELTA #若是有email会把email配置上 if hasattr(user, 'email'): payload['email'] = user.email if isinstance(user.pk, uuid.UUID): #若是有user_id会把pk值配置上 payload['user_id'] = str(user.pk) payload[username_field] = username #源码 settings= 'JWT_EXPIRATION_DELTA':datetime.timedelta(seconds=300) , }
api_settings.JWT_ENCODE_HANDLER --> 加密的具体实现 def jwt_encode_handler(payload): #payload传入 key = api_settings.JWT_PRIVATE_KEY or jwt_get_secret_key(payload) #加盐 #SECRET_KEY = '+gr4bbq8e$yqbd%n_h)2(osz=bmk1x2+o6+w5g@a4r1#3%q1n*' return jwt.encode( payload,#类型信息头信息 key,#加盐 #默认封装传入了hs256 api_settings.JWT_ALGORITHM # 'JWT_ALGORITHM': 'HS256', ).decode('utf-8') encode默认继承父类的 父类的encode方法 # Header header = {'typ': self.header_typ, 'alg': algorithm} #self.header_typ #-- 》 header_typ = 'JWT' signing_input = b'.'.join(segments)#把类型信息和数据用.的形式拼接到了一块儿 try: alg_obj = self._algorithms[algorithm]#执行算法 key = alg_obj.prepare_key(key) signature = alg_obj.sign(signing_input, key)#把拼接起来的值进行二次加密 成为第三个信息 ''' except KeyError: if not has_crypto and algorithm in requires_cryptography: raise NotImplementedError( "Algorithm '%s' could not be found. Do you have cryptography " "installed?" % algorithm ) else: raise NotImplementedError('Algorithm not supported') ''' segments.append(base64url_encode(signature))#再把第三个信息放入列表 对第三个信息进行base64进行加密 return b'.'.join(segments)#用.的形式再把第三个数据拼接起来 进行返回 98qow39df0lj980945lkdjflo.saueoja8979284sdfsdf.asiuokjd978928375
将token分割成 header_segment、payload_segment、crypto_segment 三部分 对第一部分header_segment进行base64url解密,获得header 对第二部分payload_segment进行base64url解密,获得payload 对第三部分crypto_segment进行base64url解密,获得signature 对第三部分signature部分数据进行合法性校验 拼接前两段密文,即:signing_input 从第一段明文中获取加密算法,默认:HS256 使用 算法+盐 对signing_input 进行加密,将获得的结果和signature密文进行比较。
通常在先后端分离时,用于作用户认证(登陆)使用的技术。 jwt的实现原理: - 用户登陆成功以后,会给前端返回一段token。 - token是由.分割的三段组成。 - 第一段:类型和算法信心 - 第二段:用户信息+超时时间 - 第三段:hs256(前两段拼接)加密 + base64url - 之后前端再次发来信息时 - 超时验证 - token合法性校验 #优点: - token只在前端保存,后端只负责校验。 - 内部集成了超时时间,后端能够根据时间进行校验是否超时。 - 因为内部存在hash256加密,因此用户不能够修改token,只要一修改就认证失败。
import uuid from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.versioning import URLPathVersioning from rest_framework import status from api import models class LoginView(APIView): """ 登陆接口 """ def post(self,request,*args,**kwargs): # 基于jwt的认证 # 1.去数据库获取用户信息 from rest_framework_jwt.settings import api_settings #头信息 jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER #第三个数据的加密 jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER user = models.UserInfo.objects.filter(**request.data).first() if not user: return Response({'code':1000,'error':'用户名或密码错误'}) ''' 'user_id': user.pk, 'username': username, 'exp': datetime.utcnow() + api_settings.JWT_EXPIRATION_DELTA ''' #头信息的处理 payload = jwt_payload_handler(user) #对三段信息的编码 加密 token = jwt_encode_handler(payload) return Response({'code':1001,'data':token}) #第二种方式 class LoginView(APIView): authentication_classes = [] def post(self,request,*args,**kwargs): # 1.根据用户名和密码检测用户是否能够登陆 user = models.UserInfo.objects.filter(username=request.data.get('username'),password=request.data.get('password')).first() if not user: return Response({'code':10001,'error':'用户名或密码错误'}) # 2. 根据user对象生成payload(中间值的数据) jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER payload = jwt_payload_handler(user) # 3. 构造前面数据,base64加密;中间数据base64加密;前两段拼接而后作hs256加密(加盐),再作base64加密。生成token jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER token = jwt_encode_handler(payload) return Response({'code': 10000, 'data': token})
开始解码
from rest_framework.views import APIView from rest_framework.response import Response # from rest_framework.throttling import AnonRateThrottle,BaseThrottle class ArticleView(APIView): # throttle_classes = [AnonRateThrottle,] def get(self,request,*args,**kwargs): # 获取用户提交的token,进行一步一步校验 import jwt from rest_framework import exceptions from rest_framework_jwt.settings import api_settings #进行解码 jwt_decode_handler = api_settings.JWT_DECODE_HANDLER #获取到加密以后的字符串 jwt_value = request.query_params.get('token') try: #对token进行解密 payload = jwt_decode_handler(jwt_value) #判断签名是否过时 except jwt.ExpiredSignature: msg = '签名已过时' raise exceptions.AuthenticationFailed(msg) #判断是否被篡改 except jwt.DecodeError: msg = '认证失败' raise exceptions.AuthenticationFailed(msg) except jwt.InvalidTokenError: raise exceptions.AuthenticationFailed() print(payload) return Response('文章列表')
'JWT_EXPIRATION_DELTA':datetime.timedelta(seconds=300) #默认五分钟有效 #自定义 import datetime JWT_AUTH = { "JWT_EXPIRATION_DELTA":datetime.timedelta(minutes=10) }
ListModelMixin
执行list方法
用于帮助开发者经过代码远程链接服务器
公钥和私钥进行链接服务器 比较方便
$ssh -copy-id -i .ssh /id ras.pub
基于用户名密码链接:
须要一直去使用输入
import paramiko # 建立SSH对象 ssh = paramiko.SSHClient() # 容许链接不在know_hosts文件中的主机 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 链接服务器 ssh.connect(hostname='192.168.159.128', port=22, username='root', password='ziwen123') # 执行命令 stdin, stdout, stderr = ssh.exec_command('df') # 获取命令结果 result = stdout.read() # 关闭链接 ssh.close()
发送数据
import requests
requests.post(
url = "http://127.0.0.1:8000/api/v1/server/",
json={'server':data,'host':'192.16.12.66'}
)
```
远程执行命令【公钥和私钥】(公钥必须提早上传到服务器)
import paramiko private_key = paramiko.RSAKey.from_private_key_file('/home/auto/.ssh/id_rsa') # 建立SSH对象 ssh = paramiko.SSHClient() # 容许链接不在know_hosts文件中的主机 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 链接服务器 ssh.connect(hostname='c1.salt.com', port=22, username='wupeiqi', key=private_key) # 执行命令 stdin, stdout, stderr = ssh.exec_command('df') # 获取命令结果 result = stdout.read() # 关闭链接 ssh.close()
远程上传和下载文件【用户名和密码】
import paramiko transport = paramiko.Transport(('hostname',22)) transport.connect(username='wupeiqi',password='123') sftp = paramiko.SFTPClient.from_transport(transport) # 将location.py 上传至服务器 /tmp/test.py sftp.put('/tmp/location.py', '/tmp/test.py') # 将remove_path 下载到本地 local_path sftp.get('remove_path', 'local_path') transport.close()
远程上传和下载文件【公钥和私钥】
import paramiko private_key = paramiko.RSAKey.from_private_key_file('/home/auto/.ssh/id_rsa') transport = paramiko.Transport(('hostname', 22)) transport.connect(username='wupeiqi', pkey=private_key ) sftp = paramiko.SFTPClient.from_transport(transport) # 将location.py 上传至服务器 /tmp/test.py sftp.put('/tmp/location.py', '/tmp/test.py') # 将remove_path 下载到本地 local_path sftp.get('remove_path', 'local_path') transport.close()
补充:经过私钥字符串也能够链接远程服务器
key = """-----BEGIN RSA PRIVATE KEY----MIIG5AIBAAKCAYEAu0fkMInsVRnIBSiZcVYhKuccWCh6hapYgB1eSOWZLz3+xFGy G5p2z8HgiHzfT838gAm+5OajuyAuE4+fHI77LXSg+pLbr1FhPVKAP+nbsnLgvHty ykZmt74CKKvZ08wdM7eUWJbkdpRNWmkwHBi99LeO0zYbHdXQ+m0P9EiWfdacJdAV RDVCghQo1/IpfSUECpfQK1Hc0126vI8nhtrvT3V9qF420U1fwW9GJrODl71WRqvJ BgSsKsjV16f0RKARESNmtA2vEdvMeutttZoO4FbvZ+iLKpcRM4LGm2+odryr8ijv dCPCLVvoDExOPuqP1dgt5MWcCWf6ZNhMwAs/yvRHAKetvo5gtz8YvzwlikopCLM7 bS6C6woyppMHfIPjoGJ6JuKpeaWtAgugOw/oVvj1rRYoCv48R13NftqhkFD1KD8z km9CjDC8hv+2DmIedtjvVwUz2QF4PN/RC/i1jo3+3rbP1DLu9emTHiortBBrpQ5o K+y4Rzv+6NusD6DHAgMBAAECggGBAJ4hTaNOUaZpZmI0rZrsxoSbL2ugghNqid9i 7MFQW89v4TWSZXi5K6iwYw3bohKYMqNJl01fENBnk4AgvJA4ig0PdP0eEzAs3pYQ mwlcRIygQvHiqkHwv7pVTS1aLUqQBfgtAazre2xEPCwitOSEX5/JfWcJQEwoxZMt k1MIF0mZc67Zy5sT/Vwn+XScnDt2jbsEBFkPfg1aDto3ZYCQS5Aj/D21j0OauUdy 1SDIYkw1Kivx0IKsX1Kg0S6OOcnX/B6YrJvisrlQDeZnWlTsTyKSVTekIybJjUHE ZgLIIbifSbTW1Bv1iCkDAJBd4Cj4txjXPIgea9ylZ39wSDSV5Pxu0t/M3YbdA26j quVFCKqskNOC+cdYrdtVSij2Ypwov67HYsXC/w32oKO7tiRqy51LAs/WXMwQeS5a 8oWDZLiYIntY4TCYTVOvFlLRtXb+1SbwWKjJdjKvdChv4eo/Ov5JEXD2FVbVC/5E Qo3jyjIrt1lrwXUdpJa0/iz4UV33wQKBwQDprCPZVCI7yK/BWTmUvCcupotNk6CC +QIKDcvVxz63YFD5nXto4uG7ywXR6pEwOwmycO0CBuouvlPdSioQ3RYi6k0EO3Ch 9dybC5RZ3MENBHROHvU3mp01EWPUYnXAwNpvknujJqfXMxyURZvvox7hOnu/s3m4 C3eCBrMMg+uqNZDbLqAymw3pMGhHVWjy5oO8eLuLeJv6er+XoSSPNb21Da7StdQS fBPQ1H0/+RXnhFJOzANc4mRZcXMCNGVZX6MCgcEAzSz3evuCRQ47AaSOrDd89jAw PgpT+PG4gWw1jFZqHTbQ8MUl3YnElOVoaWRdIdDeslg9THg1cs5Yc9RrbIibyQjV F9k/DlXGo0F//Mgtmr7JkLP3syRl+EedRbu2Gk67XDrV7XIvhdlsEuSnEK9xOiB6 ngewM0e4TccqlLsb6u7RNMU9IjMu/iMcBXKsZ9Cr/DENmGQlTaRVt7G6UcAYGNgQ toMoCQWjR/HihlZHssLBj9U8uPyD38HKGy2OoXyNAoHBAKQzv9lHYusJ4l+G+IyJ DyucAsXX2HJQ0tsHyNYHtg2cVCqkPIV+8UtKpmNVZwMyaWUIL7Q98bA5NKuLIzZI dfbBGK/BqStWntgg8fWXx90C5UvEO2MAdjpFZxZmvgJeQuEmWVVTo5v4obubkrF5 ughhVXZng0AOZsNrO8Suqxsnmww6nn4RMVxNFOoTnbUawTXezUN71HfWa+38Ybl0 9UNWQyR0e3slz7LurrkWqwrOlBwlBrPtrsCflUbWVOXR6wKBwDFq+Dy14V2SnOG7 aeXPA5kkaCo5QJqAVglOL+OaWLqqnk6vnXwrl56pVqmz0762WT0phbIqbe02CBX1 /t3IVYVpTDIPUGG6hTqDJzmSWXGhLFlfD3Ulei3/ycCnAqh5eCUxwp8LVqjtgltW mWqqZyIx+nafsW/YgWqyYu4p1wKR/O+x5hSbsWDiwfgJ876ZgyMeCYE/9cAqqb6x 3webtfId8ICVPIpXwkks2Hu0wlYrFIX5PUPtBjJZsb00DtuUbQKBwF5BfytRZ0Z/ 6ktTfHj1OJ93hJNF9iRGpRfbHNylriVRb+hjXR3LBk8tyMAqR4rZDzfBNfPip5en 4TBMg8UATf43dVm7nv4PM2e24CRCWXMXYl7G3lFsQF/g7JNUoyr6bZQBf3pQcBw4 IJ38IcKV+L475tP4rfDrqyJz7mcJ+90a+ai5cSr9XoZqviAqNdhvBq5LjGOLkcdN bS0NAVVoGqjqIY/tOd2NMTEF6kVoYfJ7ZJtjxk/R3sdbdtajV3YsAg== -----END RSA PRIVATE KEY-----""" import paramiko from io import StringIO private_key = paramiko.RSAKey(file_obj=StringIO(key)) # 建立SSH对象 ssh = paramiko.SSHClient() # 容许链接不在know_hosts文件中的主机 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 链接服务器 ssh.connect(hostname='192.168.16.85', port=22, username='root', pkey=private_key) # 执行命令 stdin, stdout, stderr = ssh.exec_command('df') # 获取命令结果 result = stdout.read() # 关闭链接 ssh.close() print(result)
生成
公司员工基于xshell链接服务器 用户名和密码 公钥和私钥(rsa)
ssh-keygen.exe -m pem 在当前用户家目录会生成: .ssh/id_rsa.pub .ssh/id_rsa
ssh-copy-id -i ~.ssh/id_rsa.pub root@192.168.16.85
ssh root@192.168.16.85
···
什么是先后端分离
固定知识返回json数据
drf组件
帮助咱们在django框架基础上快速搭建遵循restful规范接口的程序
drf组件的功能
解析器,解析请求体中的数据,将其变成咱们想要的格式.request.data
序列化 对对象或者对象列表(queryset)进行序列化 操做以及表单验证的功能
视图 继承APIView(在内部apiview继承了django的view)
postman
模拟浏览器进行发送请求
查找模板的顺序
优先根目录下的templates retframework 有一个templates 根据app的注册顺序去每一个app的templates目录中找
import uuid str(uuid.uuid4())#随机字符串 models.表名.objects.all().none() 返回是一个none 添加数据库 models.Disk.objects.create(**以后的字典,外键id=外键id) settings里面的基本都是用import importlib import importlib 利用字符串直接得到模块
models.py input的blank=TRUE验证能够为空 null=True数据库能够为空 ,unique,惟一 charfiled choice=sex_type(等于一个元组) id是值 名字是键 ((键,值),(键,值)) max_length=16 verbose_name=’QQ昵称‘ 和label同样 显示做用于admin能够不写 models.forekey('self')自关联 multiselectField('咨询')须要第三方模块下载 多选下拉框 verbose_name='客户信息表' admin建立表的时候的表名显示的数据 unique_together=联合惟一 下载第三方模块 djangoMultiSelectField course = MultiSelectField("咨询课程", choices=course_choices) #多选,而且存成一个 列表的格式,经过modelform来用的时候,会成为一个多选框
把数据比较多的平行分红两个表(分出来一个详细表)
mysqldb是一个接口链接到mysql数据库服务器
#请求报文 由客户端发送,其中包含和许多的信息,而 django 将这些信息封装成了 HttpRequest 对 象, 请求报文 由客户端发送,其中包含和许多的信息,而 django 将这些信息封装成了HttpRequest 对象 1.该对象由 HttpRequest 祖类建立。 这个是wsgi是从属于类 2.每个请求都会生成一个 HttpRequest 对象, 3.django会将这个对象自动传递给响应的视图函数, 4.视图函数第一个参数传给视图函数。这个参数就是django视图函数的第一个参数,一般写成request。 5.通常视图函数约定俗成地使用 request 参数承接这个对象
book_obj= 第一种 book_obj.objects.remove(1) 第二种 clear 清空 第三种 set(['1','5']) # 先清除再添加,至关于修改 #把原来的删除从新添加进去,注意不是原位置
Field required=True, 是否容许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} validators=[], 自定义验证规则 localize=False, 是否支持本地化 disabled=False, 是否能够编辑 label_suffix=None Label内容后缀 CharField(Field) max_length=None, 最大长度 min_length=None, 最小长度 strip=True 是否移除用户输入空白 IntegerField(Field) max_value=None, 最大值 min_value=None, 最小值 FloatField(IntegerField) ... DecimalField(IntegerField) max_value=None, 最大值 min_value=None, 最小值 max_digits=None, 总长度 decimal_places=None, 小数位长度 BaseTemporalField(Field) input_formats=None 时间格式化 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 DurationField(Field) 时间间隔:%d %H:%M:%S.%f ... RegexField(CharField) regex, 自定制正则表达式 max_length=None, 最大长度 min_length=None, 最小长度 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} EmailField(CharField) ... FileField(Field) allow_empty_file=False 是否容许空文件 ImageField(FileField) ... 注:须要PIL模块,pip3 install Pillow 以上两个字典使用时,须要注意两点: - form表单中 enctype="multipart/form-data" - view函数中 obj = MyForm(request.POST, request.FILES) URLField(Field) ... BooleanField(Field) ... NullBooleanField(BooleanField) ... ChoiceField(Field) ... choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),) required=True, 是否必填 widget=None, 插件,默认select插件 label=None, Label内容 initial=None, 初始值 help_text='', 帮助提示 ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查询数据库中的数据 empty_label="---------", # 默认空显示内容 to_field_name=None, # HTML中value的值对应的字段 limit_choices_to=None # ModelForm中对queryset二次筛选 ModelMultipleChoiceField(ModelChoiceField) ... django.forms.models.ModelMultipleChoiceField TypedChoiceField(ChoiceField) coerce = lambda val: val 对选中的值进行一次转换 empty_value= '' 空值的默认值 MultipleChoiceField(ChoiceField) ... TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 对选中的每个值进行一次转换 empty_value= '' 空值的默认值 ComboField(Field) fields=() 使用多个验证,以下:即验证最大长度20,又验证邮箱格式 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) MultiValueField(Field) PS: 抽象类,子类中能够实现聚合多个字典去匹配一个值,要配合MultiWidget使用 SplitDateTimeField(MultiValueField) input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中 path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 容许文件 allow_folders=False, 容许文件夹 required=True, widget=None, label=None, initial=None, help_text='' GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,若是是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 SlugField(CharField) 数字,字母,下划线,减号(连字符) ... UUIDField(CharField) uuid类型
traditional:true, 在ajax设置该属性就能够日后端传递数组
ajax的traditional属性 jquery框架的ajax参数除了经常使用的 $.ajax({ url: 'xxx', type: 'xxx', data: 'xxx', success: 'xxx' ... }) 另外还有一个参数须要特别注意下traditional默认值是false。 ajax作数据处理时,是经过jQuery.param( obj, traditional )该方法进行处理。 jquery1.4版本之后 traditional参数,默认false的时候若是是{a:{b:'value'}}是处理成a[b],这样形式,若是是数组:data:{a:[1,2]},是解析成a[]=1&a[]=2,这种方式后台确实要作兼容(取a[b]或a[])来取值。 在数组状况下把traditional参数设置成true,是解析成a=1&a=2,对后台直接经过a拿数据。可是实验了下object状况,把traditional设置成true,转成了a=[object+Object],这样就是费的了。false时解析成上面的形式应该就是类型指示做用,我看到这种格式就知道请求数据是Array仍是object了,true就是按照form提交的方式传值。 当须要把多个checkbox的value值经过ajax传到servlet时就须要加上traditional参数而且为true,以下代码: //批量删除 $("#alldel").click(function () { var ids = $(".che:checked"); var items = new Array(); for (var i=0;i<ids.size();i++){ items.push(ids[i].value); } if (confirm("您肯定要删除选中数据吗?")) { $.ajax({ type: "post", url: "UserServlet?action=deleteAll", data:{items:items}, //防止深度序列化 traditional :true, async: true, success: function(data) { var da = JSON.parse(data); alert(da.msg) app.count(); }, error: function(data) { console.info("error: " + data.responseText); } }); } });
在后台咱们就能够用 String[] items = request.getParameterValues("items") 进行接收前端传过来的数组,以下图: request.POST.get() #获取值 这样就能够用SQL语句 delete from xxx where id in(x,x) 多参数的形式进行操做了。
表结构不多有一对一之间的关系
不少时候都是1对多 多对多之间的关系
在html里就能够直接清除了<input type="text" autocomplete="off"> input 的autocomplete属性默认是on:其含义表明是否让浏览器自动记录以前输入的值 off:则关闭记录
# 多对多 若是post请求多个值使用getlist(字段) 写法 obj=request.POST.getlist("author_id") #取得的是一个列表 # 海狗的怂逼人生 是哪些做者写的 -- 正向查询 obj = models.Book.objects.filter(title='海狗的怂逼人生').first() ret = obj.authors.all()#能够直接查询到做者对应的名字 (直接查询到) print(ret) #<QuerySet [<Author: 王洋>, <Author: 海狗>]> for i in ret: print(i.name) # 查询一下海狗写了哪些书 -- 反向查询 obj = models.Author.objects.filter(name='海狗').first() ret = obj.book_set.all() print(ret) for i in ret: print(i.publishs.name) print(i.title) return HttpResponse('ok')
1.filter双下方法
1.2用i不区分大小写
2.下拉框(select)选择出来的就是列表
若是post请求多个值使用getlist(字段) 写法 obj=request.POST.getlist("author_id") #取得的是一个列表
request.Get.get()取到的值是 ?后面对应
此写法的好处不须要添加多余的路径 不用分组路径 127.0.0.1/home?id=3 request.post.get()取到值是3
mysqldb是一个接口链接到mysql数据库服务器从python
from wsgiref.simple_server import make_server # wsgiref自己就是个web框架,提供了一些固定的功能(请求和响应信息的封装,不须要咱们本身写原生的 socket了也不须要我们本身来完成请求信息的提取了,提取起来很方便) #函数名字随便起 def application(environ, start_response): ''' :param environ: 是所有加工好的请求信息,加工成了一个字典,经过字典取值的方式就能拿到不少 你想要拿到的信息 :param start_response: 帮你封装响应信息的(响应行和响应头),注意下面的参数 :return: ''' start_response('200 OK', [('Content-Type', 'text/html'),('k1','v1')]) print(environ) print(environ['PATH_INFO']) #输入地址127.0.0.1:8000,这个打印的是'/',输入的是 127.0.0.1:8000/index,打印结果是'/index' return [b'<h1>Hello, web!</h1>'] #和我们学的socketserver那个模块很像啊 httpd = make_server('127.0.0.1', 8080, application) print('Serving HTTP on port 8080...') # 开始监听HTTP请求: httpd.serve_forever()#send application的返回值 一个列表
#请求报文 由客户端发送,其中包含和许多的信息,而 django 将这些信息封装成了 HttpRequest 对 象, 请求报文 由客户端发送,其中包含和许多的信息,而 django 将这些信息封装成了HttpRequest 对象 1.该对象由 HttpRequest 祖类建立。 这个是wsgi是从属于类 2.每个请求都会生成一个 HttpRequest 对象, 3.django会将这个对象自动传递给响应的视图函数, 4.视图函数第一个参数传给视图函数。这个参数就是django视图函数的第一个参数,一般写成request。 5.通常视图函数约定俗成地使用 request 参数承接这个对象
print(requset) <WSGIRequest: GET '/app01/library/'>
book_obj= 第一种 book_obj.objects.remove(1) 第二种 clear 清空 第三种 set(['1','5']) # 先清除再添加,至关于修改 #把原来的删除从新添加进去,注意不是原位置
$(".btn-danger").on("click", function() { swal({ title: "你肯定要删除吗?", text: "删除可就找不回来了哦!", #标签显示的 type: "warning", showCancelButton: true, confirmButtonClass: "btn-danger", confirmButtonText: "删除", cancelButtonText: "取消", closeOnConfirm: false }, function() { # 注意当前的this是指谁调用了这个 var deleteId = $(this).parent().parent().attr("data_id"); $.ajax({ url: "/delete_book/", type: "post", data: { "id": deleteId }, success: function(data) { if (data.status === 1) { swal("删除成功!", "你能够准备跑路了!", "success"); } else { swal("删除失败", "你能够再尝试一下!", "error") } } }) }); })
falask路由会用 装饰器的原理 开放封闭原则 在不改变原函数代码的前提下, 在函数前函数后增长新的功能 手写简单装饰器 def wrapper(f): def inner( * args, ** kwargs): return f( * args, ** kwargs) return inner 补充: import functools def wrapper(f): @functools.wraps(f) # 加了以后保留函数元数据( 名字和注释) def inner(*args,**kwargs): return f(*args,**kwargs) return inner ''' 1.执行wrapper 2.返回inner从新赋值index ''' index=wrapper(index) @wrapper def index(a1, a2): return a1+a2 print(index.__name__) #查看函数名 print(index.__doc__) #查看注释 1.不加内部装饰器functools.wraps index执行的是inner 2.加了保留函数元数据(名字和注释)
verbose_name#admin显示的名字 之后在model form 和form中使用 写法 name=models.CharField(verbose_name="出版社名称",max_length=32)
这种写法的好处: 防止/index/下的子页面访问路径被以前路径的匹配 在urls.py路由中写法 urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/$',views.index) ]
1.先从根目录去找templates 2.在根据app注册顺序去每一个app的templates中找(从上往下)