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判断,提高了代码的可读性