drf面試題及總結
1.什么是前后端分離
2.什么是restful規范
3.模擬瀏覽器進行發送請求的工具
4.查找模板的順序
5.什么是drf組件
6.drf組件提供的功能
7.drf繼承過哪些視圖類?以及他們之間的區別?
8.GenericAPIView視圖類的作用
9.drf版本的實現過程?
10.drf組件認證的實現過程?
11.drf組件權限的實現過程?
12.drf組件中節流的實現方式?
13.序列化時many=True和many=False的區別?
14.drf各個功能的使用程度,不代表重要程度
15.什么是jwt? 它的優勢是什么?
16.裝飾器
17.面向對象中基於繼承+異常處理來做的約束
18.面向對象封裝
19.面向對象繼承
20.反射
21.ajax請求寫法
22.跨域問題
23.如何解決ajax+跨域?
24.常見的HTTP請求方法
25.http請求中Content-type請求頭
26.django中F查詢
27.django中獲取空Queryset
28.基於django的fbv和cbv都能實現遵循restful規范的接口
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 </span>=<span style="color: #000000;"> api_settings.DEFAULT_FILTER_BACKENDS pagination_class </span>=<span style="color: #000000;"> api_settings.DEFAULT_PAGINATION_CLASS </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_queryset(self): </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self.queryset </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_serializer_class(self): </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self.serializer_class </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> filter_queryset(self, queryset): </span><span style="color: #0000ff;">for</span> backend <span style="color: #0000ff;">in</span><span style="color: #000000;"> list(self.filter_backends): queryset </span>=<span style="color: #000000;"> backend().filter_queryset(self.request, queryset, self) </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> queryset @property </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> paginator(self): </span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span> hasattr(self, <span style="color: #800000;">'</span><span style="color: #800000;">_paginator</span><span style="color: #800000;">'</span><span style="color: #000000;">): </span><span style="color: #0000ff;">if</span> self.pagination_class <span style="color: #0000ff;">is</span><span style="color: #000000;"> None: self._paginator </span>=<span style="color: #000000;"> None </span><span style="color: #0000ff;">else</span><span style="color: #000000;">: self._paginator </span>=<span style="color: #000000;"> self.pagination_class() </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self._paginator他相當於提供了一些規則,建議子類中使用固定的方式獲取數據,例如:
class ArticleView(GenericAPIView):
queryset = models.User.objects.all()</span><span style="color: #0000ff;">def</span> get(self,request,*args,**<span style="color: #000000;">kwargs): query </span>=<span style="color: #000000;"> 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方法,<br>里面會先判斷是否有versioning_class,如果沒有就返回(None,None),就代表沒有版本,如果有就執行versioning_class(URLPathVersioning)類中的determine_version方法,它會返回版本,里面會判斷,<br>如果獲取到的version為空則返回默認版本,並且還要判斷版本是否存在允許出現的版本列表中,返回版本之后,再把版本號和版本類分別賦值給request.version和request.versioning_scheme</span></pre>
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 ...</span><span style="color: #0000ff;">def</span> initialize_request(self, request, *args, **<span style="color: #000000;">kwargs): </span><span style="color: #800000;">"""</span><span style="color: #800000;"> Returns the initial request object. </span><span style="color: #800000;">"""</span><span style="color: #000000;"> parser_context </span>=<span style="color: #000000;"> 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</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_seriliser_class(self): </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> 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<span style="color: #0000ff;">def</span><span style="color: #000000;"> post(): </span><span style="color: #0000ff;">pass</span><span style="color: #000000;">
FBV和CBV的區別:
FBV顧名思義就是函數處理請求,代碼冗余比較多,不是面向對象編程
CBV則是使用類中的不同方法來處理請求,迎合了python所推崇的面向對象編程思想。
相比FBV的優點:
1、提高了代碼的復用性,可以使用面向對象的計算,比如Mixin(多繼承)
2、可以用不同的函數針對不同的http請求方法處理,而不是通過過多的if判斷,提高了代碼的可讀性









