drf面試題及總結
阿新 • • 發佈:2020-08-23
1、什麼是前後端分離
前端:整個頁面顯示以及頁面的互動邏輯,用ajax和node作為互動。其中node作為中間層 後端:提供api介面,利用redis儲存session,與資料庫互動 步驟: 1)客戶端(瀏覽器)向node請求頁面互動。 2)node向後端(這裡用java)轉發請求。java在傳送請求到資料庫。 3)java返回結果給node。node返回頁面,提供資料。 node: node主要是為了分層開發,前端不需要知道後端是怎麼提供資料,怎麼操作。後端也不需要知道node是怎麼操作,前端是怎麼部署。前端可以利用node自己作處理。 node本身有著非同步,非阻塞I/o。在處理併發量比較大的資料請求上有很大的優勢。
2、什麼是restful規範
restful規範是一套規則,用於API中之間進行資料交換的約定。 它的具體規則有: 1、https代替http,保證資料傳輸時的安全 2、在url中一般要體現api標識,這樣看到url就知道他是一個api 建議:https://www.zdr.com/api/...(不會存在跨域問題) 3、在介面中要體現版本,可放在url中也可以放在請求頭中 建議:https://www.zdr.com/api/v1/... 4、restful也稱為面向資源程式設計,視網路上的一切都是資源,對資源可以進行操作,所以一般資源都用名詞 5、如果要加入一些篩選條件,可以新增在url中 https://www.zdr.com/api/v1/user/?page=1&type=9 6、根據method請求方法不同做不同操作 get/post/put/patch/delete 7、根據請求方法不同返回不同的值 get全部/post返回新增的值/put/patch/delete不返回值 8、給使用者返回狀態碼 - 200——成功 - 300——301是永久重定向,302是臨時重定向 - 400——403拒絕中介軟體的csrftoken認證 /404找不到 - 500——服務端程式碼錯誤 9、操作異常時,要返回錯誤資訊 { error: "Invalid API key" } 10、對於下一個請求要返回一些介面: Hypermedia AP { 'id':2, 'name':'alex', 'age':19, 'depart': "http://www.luffycity.com/api/user/30/" }
3、模擬瀏覽器進行傳送請求的工具
postman
4、查詢模板的順序
優先查詢根目錄下:templates 根據app的註冊順序去每個app的templates目錄中找
5、什麼是drf元件
drf的全稱是Django RESTful Framework 它是一個基於django開發的元件,本質是一個django的app drf可以幫我們快速開發出一個遵循restful規範的程式
6、drf元件提供的功能
免除csrf認證 檢視(三種:(1)APIView,(2)ListAPIview,(3)ListModelMixin) 版本處理 認證 許可權 節流(頻率限制) 解析器 篩選器 分頁 序列化和資料校驗:可以對QuerySet進行序列化,也可以對使用者提交的資料進行校驗——展示特殊的資料 depth source:無需加括號,在原始碼內部會去判斷是否可執行,如果可執行自動加括號。【多對一、一對一/choice】 SerializerMethodField定義鉤子方法【多對多】 渲染器:可以幫我們把json資料渲染到drf自己的頁面上。
7、drf繼承過哪些檢視類?以及他們之間的區別?
第一種:APIView 第一種遵循了CBV的模式,裡面的功能比較多但是需要自己寫的程式碼也有很多 提供了免除csrf認證,版本處理、認證、許可權、節流、解析器、篩選器、分頁、序列化、渲染器 第二種:ListAPIView,RetrieveAPIView,CreateAPIView,UpdateAPIView,DestroyAPIView 第二種則在第一種的基礎上,封裝了許多我們需要自己的寫的程式碼,許多功能的實現只需要給專屬的變數名賦值就可以實現該功能 第三種:GenericViewSet、ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin 第三種則重構了APIView中的as_view()方法,結合請求方法和不同Mixin類的方法名從而進行執行不同的功能。與前面兩種最主要的區別是url路由中as_view()方法中需要傳值。 目前使用的主要目的是把第二種的bug(查詢全部資料的功能和查詢單個數據的功能無法在一個類中實現)實現在一個類中!
8、GenericAPIView檢視類的作用
總結:GenericAPIView主要為drf內部幫助我們提供增刪改查的類LIstAPIView、CreateAPIView、UpdateAPIView、提供了執行流程和功能,
我們在使用drf內建類做增刪改查時,就可以通過自定義 靜態欄位(類變數)或重寫方法(get_queryset、get_serializer_class)來進行更高階的定製。 他提供了一些規則,例如: 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...
9、drf版本的實現過程?
# drf自帶的版本類 "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning", # 允許出現的版本 "ALLOWED_VERSIONS": ['v1', 'v2'], # 如果沒有傳版本,可以使用預設版本 default_version = api_settings.DEFAULT_VERSION # 設定url中獲取版本的變數,預設是version version_param = api_settings.VERSION_PARAM 當前端來請求時,執行了as_views()方法,如果設定了全域性版本或者進入了設定了版本的功能函式,則會先執行APIView類中的dispatch方法,之後再執行initial方法,然後進入了self.determine_version方法,
裡面會先判斷是否有versioning_class,如果沒有就返回(None,None),就代表沒有版本,如果有就執行versioning_class(URLPathVersioning)類中的determine_version方法,它會返回版本,裡面會判斷,
如果獲取到的version為空則返回預設版本,並且還要判斷版本是否存在允許出現的版本列表中,返回版本之後,再把版本號和版本類分別賦值給request.version和request.versioning_scheme
10、drf元件認證的實現過程?
當用戶進行登入的時候,運行了登入類的as_view()方法, 1、進入了APIView類的dispatch方法 2、執行了self.initialize_request這個方法,是重定義request,並且得到了自己定義的認證類物件 3、執行self.initial方法中的self.perform_authentication,裡面運行了user方法 4、再執行了user方法裡面的self._authenticate()方法 5、然後執行了自己定義的類中的authenticate方法,自己定義的類繼承了BaseAuthentication類,裡面有 authenticate方法,如果自己定義的類中沒有authenticate方法會報錯。 6、把從authenticate方法得到的user和auth賦值給user和auth方法 7、這兩個方法把user和auth的值賦值給了request.user:是登入使用者的物件,request.auth:是認證的資訊字典
11、drf元件許可權的實現過程?
當用戶執行一個業務的時候,運行了as_view方法 1、進入了APIView類的dispatch方法 2、進入self.initial方法中的self.check_permissions(request)方法 3、裡面執行了for迴圈,把每個許可權類例項化物件, 4、執行自己定義的許可權類裡面的has_permission方法,裡面會判斷request.user是否存在 5、不存在就返回False,存在就返回True 6、之後執行self.permission_denied報錯方法,返回的是False就報錯,可以自定義報錯資訊,在has_permission方法中寫message = {"status": False, "error": "登入成功之後才能評論"},就實現了自定義報錯 7、如果返回的是True就讓他進入功能
12、drf元件中節流的實現方式?
匿名使用者通過ip地址來控制訪問頻率,已登入使用者通過id來控制 首先要設定配置檔案: # 也可以設定全域性, "DEFAULT_THROTTLE_CLASSES":["rest_framework.throttling.AnonRateThrottle",] # 設定訪問頻率——一分鐘10次 "DEFAULT_THROTTLE_RATES": { "anon":"10/m" } - 實現原理 把所有登入記錄時間放在一個列表中,當用戶請求網頁的時候,用現在的時間減去約束的時間間隔,然後把小於這個時間記錄排除,再計算出時間間隙的記錄條數,如果其中的條數小於規定的條數則可以訪問並且把當前時間新增進列表中,如果大於或等於則不讓其訪問。 - 具體流程 當用戶請求網頁的時候,後臺允許該介面的url中的as_views(),執行原始碼的APIView中的dispatch方法,執行initial方法,裡面的check_throttles方法,迴圈執行節流類中的allow_request方法,但是AnonRateThrottle等類中沒有,去執行SimpleRateThrottle類中的allow_request方法,裡面就是實現原理中的程式碼,如果可以訪問返回True,如果不讓訪問則返回False,之後返回check_throttles,如果是False則執行SimpleRateThrottle類中的wait方法得到需要等待的時間在頁面上顯示!
13、序列化時many=True和many=False的區別?
在使用APIView時,資料展示的時候序列化多個數據的時候用many=True,序列化單個數據的時候用many=False 案例: category_all = models.Category.objects.all() ser_category = serializer.HomeCategorySerializer(instance=category_all, many=True) article_obj = models.Article.objects.filter(id=pk).first() ser = serializer.OneArticleSerializer(instance=article_obj, many=False)
14、drf各個功能的使用程度,不代表重要程度
***** 解析器:request.query_parmas/request.data 檢視 序列化 渲染器:Response **** request物件封裝 版本處理 分頁處理 *** 認證 許可權 節流
15、什麼是jwt? 它的優勢是什麼?
jwt的全稱是json web token, 一般用於使用者認證 jwt的實現原理: - 使用者登入成功之後,會給前端返回一段token。 - token是由.分割的三段組成。 - 第一段header:型別+演算法+base64url加密 - 第二段paylod:使用者資訊+超時時間+base64url加密 - 第三段sign:hs256(前兩段拼接)加密 + base64url - 以後前端再次發來資訊時 - 超時驗證 - token合法性校驗 優勢: - token只在前端儲存,後端只負責校驗。 - 內部集成了超時時間,後端可以根據時間進行校驗是否超時。 - 由於內部存在hash256加密,所以使用者不可以修改token,只要一修改就認證失敗。
16、裝飾器
應用區域: - 在django中csrftoken認證中使用了 - 在flask中的路由url中也使用了 標準裝飾器: def outer(func): def inner(*args,**kwargs): return func(*args,**kwargs) return inner @outer def index(a1): pass index()
17、面向物件中基於繼承+異常處理來做的約束
在drf的版本、認證、許可權、節流的原始碼中都大量使用了面向物件中的繼承和異常處理 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
18、面向物件封裝
drf原始碼中的APIView的dispatch中有個self.initialize_request,它返回了一個Request類,它封裝了django的request和認證物件列表等其他引數 事例: 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 )
19、面向物件繼承
django中原始碼大量使用了面向物件的繼承 尤其是drf中的繼承關係最為明顯 事例: 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()
20、反射
應用場景: 1、django中的View類的dispatch通過接收到的請求方法變為小寫從而使用反射得到類中的相對應方法,比如get方法。 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) 2、django中的settings配置檔案的原始碼也使用了反射,並且結合了 import importlib self.SETTINGS_MODULE = settings_module # 獲取到settings檔案的路徑 mod = importlib.import_module(self.SETTINGS_MODULE) # 通過importlib.import_module獲取到settings檔案物件 for setting in dir(mod): # 迴圈獲取到settings檔案物件裡面的屬性 if setting.isupper(): # 得到大寫的屬性名 setting_value = getattr(mod, setting) # 得到屬性值比如中介軟體等其他配置屬性
21、ajax請求寫法
可模擬任何請求方式向後端傳送請求 $.ajax({ url:'地址', type:'GET', data:{...}, success:function(arg){ console.log(arg); } })
22、跨域問題
瀏覽器具有”同源策略的限制“,導致傳送ajax請求
+跨域
存在無法獲取資料,只有ajax才能導致跨域,html中的src不會,導致跨域的原因有域名不同或者不同埠、http與https互相傳送
-
簡單請求,傳送一次請求
-
複雜請求,先options請求做預檢,然後再發送真正的請求
23、如何解決ajax+跨域?
CORS(跨域資源共享):是一種使用額外的 HTTP 頭部來允許瀏覽器可以在一個不同域的網站內獲取另一個域下的伺服器資源的機制。 本質是設定響應頭。
24、常見的HTTP請求方法
get:讓後端傳給前端想要的資料 post:前端傳資料給後端讓其新增記錄 put:前端傳資料和篩選資料的依據給後端讓其更新記錄 patch:前端傳區域性資料和篩選資料的依據給後端讓其更新區域性記錄 delete:前端傳篩選資料的依據給後端讓其刪除記錄 options:是跨域問題中的複雜請求預檢的請求
25、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有值
26、django中F查詢
F可以提取某個欄位的值,可以用來比較兩個欄位值的判斷,可以更新某個欄位的值 Q用來表示條件,使用Q物件來組成各種關係的條件,|=or
27、django中獲取空Queryset
models.User.object.all().none()
28、基於django的fbv和cbv都能實現遵循restful規範的介面
FBV: def user(request): if request.method == "GET": pass CBV: class UserView(View): def get(): pass def post(): pass FBV和CBV的區別: FBV顧名思義就是函式處理請求,程式碼冗餘比較多,不是面向物件程式設計 CBV則是使用類中的不同方法來處理請求,迎合了python所推崇的面向物件程式設計思想。 相比FBV的優點: 1、提高了程式碼的複用性,可以使用面向物件的計算,比如Mixin(多繼承) 2、可以用不同的函式針對不同的http請求方法處理,而不是通過過多的if判斷,提高了程式碼的可讀性