常见的面试题

一、Django的请求生命周期

  1.wsgi,它就是socket的服务端,用于接收用户请求并将请求进行初次封装,然后讲求交给(Flask,Django)进行二次封装。

  2.中间件,帮助我们对请求进行校验或在请求对象中添加其他相关数据,例如:csrf、request.session

  3.路由匹配

  4.视图函数,在视图函数中进行业务逻辑的处理,可能涉及到:ORM、templates ==>>渲染

  5.中间件,对响应的数据进行处理。

  6.wsgi,将响应的内容发给浏览器。

  Django的流程图:

二、什么是wsgi?  

web服务网关接口,wsgi是一个协议,实现该写一个的模块:
- wsgiref
- werkzeug
实现其协议的模块本质上就是socket服务端用于接收用户请求,并处理。
一般web框架基于wsgi实现,这样实现关注点分离。

wsgiref示例:
from wsgiref.simple_server import make_server
 
def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ]
             
             
if __name__ == '__main__':
    httpd = make_server('127.0.0.1', 8000, run_server)
    httpd.serve_forever()
        
werkzeug示例:
from werkzeug.wrappers import Response
from werkzeug.serving import run_simple

def run_server(environ, start_response):
    response = Response('hello')
    return response(environ, start_response)

if __name__ == '__main__':
    run_simple('127.0.0.1', 8000, run_server)

三、什么是HTTP协议?

  HTTP协议就是一种传输数据的格式,是基于TCP的之上的。原来学习django框架,是从socket服务端开始学起。

  自己创造一个socket服务器来充当网站,浏览器充当socket客户端。

  更能够明白http协议到底是什么:

  ----- 请求头  请求体

  ----- 响应头  响应体

  一次请求响应后,断开链接。(无状态,短连接)

四、常见的请求头

  - Content-Type 
  - User-Agent:      值得不同,返回不同的页面。(根据设备)
  - referer:      可以做图片防盗链。
  - Host:          当前域名
  - cookies:     携带在请求头里面

五、常见的请求体?

  Form表单提交:
    POST /index http1.1 host:www.luffycity.com... username=alex&password=123&...
  Ajax请求:
    POST /index http1.1 host:www.luffycity.com... username=alex&password=123&...
    POST /index http1.1 host:www.luffycity.com... {“username”:"alex","password":123}

  补充:django中获取请求体
     - request.POST   只能获取这种可是的(nusername=alex&password=123)
     - request.body   能够获得 ({“username”:"alex","password":123})

  常见的请求方法:

    GET/POST/DELETE/PUT/PATCH/OPTIONS

六、中间件

  中间件有五种方法:

    process_request(self,request)
    process_view(self, request, callback, callback_args, callback_kwargs)
    process_template_response(self,request,response)
    process_exception(self, request, exception)
    process_response(self, request, response

  中间件的应用场景:

    1.登录认证,不再需要在每个函数中添加装饰器。

    2.权限,当用户登录的时候获得当前用户的所有权限并放入session,然后再次访问其他页面,获取当前url并在session中进行匹配。如果没有匹配成功,则在中间件返回“无权访问”

    3.跨域

    4.使用中间件做过什么?

      (1)内置  csrf   sesson

      (2)自定义   登录认证  权限  cors

七、为什么会有跨域?

  浏览器具有同源策略才出现跨域。

    同源策略:

      开放:src

      禁止:ajax

  解决跨域的两种方法:

    1、jsonp:在客户端动态的创建一个script标签

      (1) 客户端:创建一个

        <script src='http://www.jxntv.cn/data/jmd-jxtv2.html'></script>

        <script>
           function func(arg){
            alert(arg);
              }
         </script>

      (2) 服务端:接收到请求并处理返回值"func('success')"相当于:

        <script>

          func('success')
        </script>

        必须与上边一致,而且jsonp只能发送GET请求

    2、cors:设置响应头

      (1)简单请求

      (2)复杂请求

        options 请求做预检

        PUT/POST

  应用:本地测试前后端分离时使用。

  在Django中的解决方案:

    1、中间件中设置响应头

    2、Django中的一个第三方组件:cors

  补充Ajax:  

    jQuery Ajax:
      $.ajax({
          ...
      })
    原生Ajax:XMLHttpRequest对象:
      var xhr = new XMLHttpRequest()

      xhr.onreadystatechange = function(){
        if(xhr.readyState == 4){
          // 已经接收到全部响应数据,执行以下操作
          var data = xhr.responseText;
          console.log(data);
        }
      };

      xhr.open('POST', "/test/", true);

      // 设置请求头
      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-  UTF-8');

      // 发送请求
      xhr.send('n1=1;n2=2;');

八、视图

  在本质的上FBV和CBV是一样的

  区别:

  FBV:url----->函数(自己定义)

  CBV:   url----->view (继承的类)

  继承的类:

    class View(object): 

    class APIView(View):

    class GenericAPIView(views.APIView):

    class GenericViewSet(ViewSetMixin, generics.GenericAPIView)

    class ModelViewSet(mixins.CreateModelMixin,

             mixins.RetrieveModelMixin,

             mixins.UpdateModelMixin,

               mixins.DestroyModelMixin,

              mixins.ListModelMixin,

             GenericViewSet):

 九、restful规范

  restful是一个规范,规定API如何编写,通过它可以让我们api更加简洁方便维护。

  例如,最直观的:

    method:   get/post/put/delete

    原来都是在url中设置的。

  除此之外还有:

    (1) api 

    (2)版本

    (3)名词(面向资源编程)

    (4)条件

    (5)响应式设置状态码

    (6)返回值    

      GET: 所有列表
        {
          code: 10000,
          data: [
            {'id':1,'title':'xx'},
            {'id':1,'title':'yy'},
            {'id':1,'title':'zzz'},
              ]
          }

      POST: 返回新增的数据
        {'id':1,'title':'xx'}

        https://www.luffycity.com/api/v2/salary/1/
      GET: 获取单条数据
        {'id':1,'title':'xx'}
      PUT:更新
        {'id':1,'title':'xx'}
      PATCH: 局部更新
        {'id':1,'title':'xx'}
      DELETE:删除

    (7)错误信息

    (8)hypermedia link

  什么是接口?

    URL

    约束:约束继承(实现)了它的类中必须含有的方法

十、django rest framework 框架

  作用:快速搭建基于restful规定的接口

   1、路由

       (1)可以通过as_view传参数,根据请求方式不同执行相应的方法

       (2)可以在url中设置一个结尾,类似于:.json

   2、视图

         帮助了开发者提供了一些类,并在类中提供了多个方法以供我们使用

   3、版本

     在url中设置version参数,用户请求时候传入参数。在request.version中获取版本,根据版本不同做不同处理

   4、认证  

      写一个类并注册到认证类,在类的authtocate方法中编写认证逻辑

          认证成功(user,auth) 

        raise AuthticateFaild(....)

          None

   5、权限

         写一个类并注册到权限类,在类的的has_permission方法中编写认证逻辑。
                      True
            False

   6、访问频率的限制   

      - 访问频率控制原理:
        匿名:
        1.1.1.1:[时间,时间,时间,时间,]
        登录:
        user:[时间,时间,时间,时间,]

        默认将访问记录放在缓存中:redis/memcached

            写一个类并注册到频率类,在类的的 allow_request/wait 方法中编写认证逻辑。

          allow_request
          True
           False 如果返回False,那么就要执行wait

   7、解析器

    根据ContentType请求头,选择不同解析器对 请求体中的数据进行解析。

      POST /index/ http1.1. host:11.11.11.11 Content-Type:url-formendo.... user=alex&age=123
      POST /index/ http1.1. host:11.11.11.11 Content-Type:application/json {....}

   8、序列化   

      对queryset序列化以及对请求数据格式校验。 

      from rest_framework.serializers import Serializer

      class XX(Serializer):
      pass
      ser =XX(queryset,many=True) # ListSerializer对象
      ser =XX(obj, many=False) # XX对象

      - 列表生成式

      - 根据字符串的形式,自动导入模块并使用反射找到模块中的类【参考:s9day108】。

   9、分页   

         对从数据库中获取到的数据进行分页处理: SQL -> limit offset
      根据页码:http://www.luffycity.com/api/v1/student/?page=1&size=10
      根据索引:http://www.luffycity.com/api/v1/student/?offset=60&limit=10
      根据加密:http://www.luffycity.com/api/v1/student/?page=erd8

      页码越大速度越慢,为什么以及如何解决?
        原因:页码越大向后需要扫描的行数越多,因为每次都是从0开始扫描。
        解决:
           限制显示的页数
           记录当前页数据ID最大值和最小值,再次分页时,根据ID现行筛选,然后再分页。

   10、渲染器

      根据URL中传入的后缀,决定在数据如何渲染到到页面上。

十一、ORM的补充

    a. 需求: 只取某n列
      queryset=[ {},{}]
      models.User.objects.all().values( 'id','name')

      queryset=[ (),()]
      models.User.objects.all().values_list( 'id','name')

      queryset=[ obj,obj]

    b. - only
      result = models.User.objects.all().only('id','name','age')

    c. - defer
      # result = models.User.objects.all().defer('id','name','age')
      for item in reuslt:
      print(item.id,item.name,item.age)

      示例:
      class Depart(models.Model):
5个部门
      title = models.CharField(...)

      class User(models.Model): 10个用户
      name = models.CharField(...)
      email = models.CharField(...)
      dp = models.FK(Depart)

      1.之前:11次单表查询

      result = User.objects.all()
      for item in result:
      print(item.name,item.dp.title)

      2. seleted_related,主动做连表查询(1次链表)

      result = User.objects.all().seleted_related('dp')
      for item in result:
      print(item.name,item.dp.title)

      问题:如果链表多,性能越来越差。

      3. prefetch_related:2次单表查询
      # select * from user ;
      # 通过python代码获取:dp_id = [1,2]
      # select * from depart where id in dp_id
      result = User.objects.all().prefetch_related('dp')
      for item in result:
      print(item.name,item.dp.title)

    注:数据量比较大,不会使用FK,允许出现数据冗余。

     - select_related,连表操作,相当于主动做join
    - prefeth_related,多次单表操作,先查询想要的数据,然后构造条件,如:id=[1,2,3],再次查询其他表根据id做条件。
    - only
    - defer
    - F 更新数据库字段
    - Q 构造复杂条件
    - 通过ORM写偏原生SQL:
    - extra
      Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
      Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
      Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
      Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

    - raw
      # 执行原生SQL
      models.UserInfo.objects.raw('select * from userinfo')

      # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
      models.UserInfo.objects.raw('select id as nid from 其他表')

      # 为原生SQL设置参数
      models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])
      name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
      Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)
    - 原生SQL

      from django.db import connection, connections
      cursor = connection.cursor() # cursor = connections['default'].cursor()
      cursor.execute("""SELECT * from auth_user where id = %s""", [1])
      row = cursor.fetchone() # fetchall()/fetchmany(..)
      PS: 选择数据库
      queryset = models.Course.objects.using('default').all()

        

  

原文地址:https://www.cnblogs.com/xu1686318405/p/9122290.html