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中找(從上往下)