s8day141
内容回顾:
   面试题:   
      1. django和flask区别?
      
      2. django请求生命周期?
      
      3. wsgi?
         
      4. 中间件都有哪些方法?以及执行流程?
      
      5. 中间件的应用?
         - 权限
         - cors
         - csrf
         - 缓存(中间件2/4)
      
      6. 视图函数
         - FBV
         - CBV
         FBV和CBV的区别?
            - 没什么区别,因为他们的本质都是函数。CBV的.as_view()返回的view函数,view函数中调用类的dispatch方法,在dispatch方法中通过反射执行get/post/delete/put等方法。
            - CBV比较简洁,GET/POST等业务功能分别放在不同get/post函数中。FBV自己做判断进行区分。
            
         CBV时的注意事项?
            - 装饰器
               from django.views import View
               from django.utils.decorators import method_decorator
               def auth(func):
                  def inner(*args,**kwargs):
                     return func(*args,**kwargs)
                  return inner
               class UserView(View):
                  
                  @method_decorator(auth)
                  def get(self,request,*args,**kwargs):
                     return HttpResponse('...')
            - csrf的装饰器要加到dispath
                  from django.views import View
                  from django.utils.decorators import method_decorator
                  from django.views.decorators.csrf import csrf_exempt,csrf_protect
                  class UserView(View):
                     @method_decorator(csrf_exempt)
                     def dispatch(self, request, *args, **kwargs):
                        return HttpResponse('...')
                     
               或
                  from django.views import View
                  from django.utils.decorators import method_decorator
                  from django.views.decorators.csrf import csrf_exempt,csrf_protect
                  @method_decorator(csrf_exempt,name='dispatch')
                  class UserView(View):
                     def dispatch(self, request, *args, **kwargs):
                        return HttpResponse('...')
               
      7. ORM操作
         models.UserInfo.objects.create()
         
         obj = models.UserInfo(name='xx')
         obj.save()
         
         models.UserInfo.objects.bulk_create([models.UserInfo(name='xx'),models.UserInfo(name='xx')])
      
         models.UserInfo.objects.all().delete()
         models.UserInfo.objects.all().update(age=18)
         models.UserInfo.objects.all().update(salary=F('salary')+1000)
         
         .filter(id__gt=1,)
         .exclude()
         
         .values()
         .values_list()
         
         .order_by()
         .order_by('-id')
         
         
         .anotate()
         
         .first()
         .laste()
         .get()
         .exsit()
         
         .reverse()
         .distinct()
         
         .extra()
         .raw()
         
         F和Q
         
         .select_related()
         
             title = models.CharField(max_length=32)
            class UserInfo(models.Model):
               name = models.CharField(max_length=32)
               email = models.CharField(max_length=32)
               ut = models.ForeignKey(to='UserType')
               ut = models.ForeignKey(to='UserType')
               ut = models.ForeignKey(to='UserType')
               ut = models.ForeignKey(to='UserType')
               
               
            # 1次SQL
            # select * from userinfo
            objs = UserInfo.obejcts.all()
            for item in objs:
               print(item.name)
               
            # n+1次SQL
            # select * from userinfo
            objs = UserInfo.obejcts.all()
            for item in objs:
               # select * from usertype where id = item.id 
               print(item.name,item.ut.title)
               
            # 1次SQL
            # select * from userinfo inner join usertype on userinfo.ut_id = usertype.id 
            objs = UserInfo.obejcts.all().select_related('ut')
            for item in objs:
               print(item.name,item.ut.title)
         
         .prefetch_related()
            # select * from userinfo where id <= 8
            # 计算:[1,2]
            # select * from usertype where id in [1,2]
            objs = UserInfo.obejcts.filter(id__lte=8).prefetch_related('ut')
            for obj in objs:
               print(obj.name,obj.ut.title)
         
         
         补充:
            无约束:
               class UserType(models.Model):
                  title = models.CharField(max_length=32)
               class UserInfo(models.Model):
                  name = models.CharField(max_length=32)
                  email = models.CharField(max_length=32)
                  # 无数据库约束,但可以进行链表
                  ut = models.ForeignKey(to='UserType',db_constraint=False)
               
            有约束:
               class UserType(models.Model):
                  title = models.CharField(max_length=32)
               class UserInfo(models.Model):
                  name = models.CharField(max_length=32)
                  email = models.CharField(max_length=32)
                  # 有数据库约束,可以进行链表
                  ut = models.ForeignKey(to='UserType')
            
         .using 
         .count 
         
         
         .only
         .defer
         
         .[1,100]
         
         .aggregate
         
         .fiter(id__in=[1,2])
         
         # 多对多 
         .add 
         .set 
         .remove 
         
      8. django的Form组件的作用?
         - 对用户请求的数据进行校验
         - 生成HTML标签
         
         PS:
            - form对象是一个可迭代对象。
            - 问题:choice的数据如果从数据库获取可能会造成数据无法实时更新
                  - 重写构造方法,在构造方法中重新去数据库获取值。
                  - ModelChoiceField字段
                     from django.forms import Form
                     from django.forms import fields
                     from django.forms.models import ModelChoiceField
                     class UserForm(Form):
                        name = fields.CharField(label='用户名',max_length=32)
                        email = fields.EmailField(label='邮箱')
                        ut_id = ModelChoiceField(queryset=models.UserType.objects.all())   
                     
                     依赖:
                        class UserType(models.Model):
                           title = models.CharField(max_length=32)
                           def __str__(self):
                              return self.title
      
      9. 多数据库相关操作
         
         python manage.py makemigraions
         
         python manage.py migrate app名称 --databse=配置文件数据名称的别名
         
         手动操作:
            models.UserType.objects.using('db1').create(title='普通用户')
            result = models.UserType.objects.all().using('default')
            
         自动操作:
            class Router1:
               def db_for_read(self, model, **hints):
                  """
                  Attempts to read auth models go to auth_db.
                  """
                  return 'db1'
               def db_for_write(self, model, **hints):
                  """
                  Attempts to write auth models go to auth_db.
                  """
                  return 'default'
            配置:
               DATABASES = {
                  'default': {
                     'ENGINE': 'django.db.backends.sqlite3',
                     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
                  },
                  'db1': {
                     'ENGINE': 'django.db.backends.sqlite3',
                     'NAME': os.path.join(BASE_DIR, 'db1.sqlite3'),
                  },
               }
               DATABASE_ROUTERS = ['db_router.Router1',]
               
            使用:
               models.UserType.objects.create(title='VVIP')
               result = models.UserType.objects.all()
               print(result)
                              
            补充:粒度更细
               class Router1:
                  def db_for_read(self, model, **hints):
                     """
                     Attempts to read auth models go to auth_db.
                     """
                     if model._meta.model_name == 'usertype':
                        return 'db1'
                     else:
                        return 'default'
                  def db_for_write(self, model, **hints):
                     """
                     Attempts to write auth models go to auth_db.
                     """
                     return 'default'
         问题: 
            app01中的表在default数据库创建
            app02中的表在db1数据库创建
            
            # 第一步:
               python manage.py makemigraions 
            
            # 第二步:
               app01中的表在default数据库创建
               python manage.py migrate app01 --database=default
            
            # 第三步:
               app02中的表在db1数据库创建
               python manage.py migrate app02 --database=db1
               
            # 手动操作:
               m1.UserType.objects.using('default').create(title='VVIP')
               m2.Users.objects.using('db1').create(name='VVIP',email='xxx')
            # 自动操作:
               配置: 
                  class Router1:
                     def db_for_read(self, model, **hints):
                        """
                        Attempts to read auth models go to auth_db.
                        """
                        if model._meta.app_label == 'app01':
                           return 'default'
                        else:
                           return 'db1'
                     def db_for_write(self, model, **hints):
                        """
                        Attempts to write auth models go to auth_db.
                        """
                        if model._meta.app_label == 'app01':
                           return 'default'
                        else:
                           return 'db1'
                  DATABASE_ROUTERS = ['db_router.Router1',]
               
               使用: 
                  m1.UserType.objects.using('default').create(title='VVIP')
                  m2.Users.objects.using('db1').create(name='VVIP',email='xxx')
         其他:
            数据库迁移时进行约束:
               class Router1:
                  def allow_migrate(self, db, app_label, model_name=None, **hints):
                     """
                     All non-auth models end up in this pool.
                     """
                     if db=='db1' and app_label == 'app02':
                        return True
                     elif db == 'default' and app_label == 'app01':
                        return True
                     else:
                        return False
                     
                     # 如果返回None,那么表示交给后续的router,如果后续没有router,则相当于返回True
                     
                  def db_for_read(self, model, **hints):
                     """
                     Attempts to read auth models go to auth_db.
                     """
                     if model._meta.app_label == 'app01':
                        return 'default'
                     else:
                        return 'db1'
                  def db_for_write(self, model, **hints):
                     """
                     Attempts to write auth models go to auth_db.
                     """
                     if model._meta.app_label == 'app01':
                        return 'default'
                     else:
                        return 'db1'
                        
今日内容:        
   1. websocket
   2. tornado
   3. redis
   
内容详细:
   1. websocket 
      回顾:
         - 什么是轮训?
            - 通过定时器让程序每隔n秒执行一次操作。
         - 什么是长轮训?
            - 浏览器向后端发起请求,后端会将请求 hang 住,最多hang 30s。
               如果一直不返回数据:则最多等待30s,紧接着用户立即再发送请求。
               如果有数据返回:则操作数据并立即再发送请求。
            PS:后台可以使用队列或redis的列表来hang主请求。
            
         - 轮训和长轮训目的?
            由于Http请求是无状态、短连接所以服务端无法向客户端实时推送消息,
            所以,我们就是可以使用:轮训和长轮训去服务端获取实时数据。
   
      作业:基于redis和长轮询实现投票。
      
      
      websocket是什么?
         websocket是一套类似于http的协议。
         扩展:
                 http协议:\r\n分割、请求头和请求体\r\n分割、无状态、短连接。
            websocket协议:\r\n分割、创建连接后不断开、 验证+数据加密;
            
         websocket本质:
            - 就是一个创建连接后不断开的socket
            - 当连接成功之后:
               - 客户端(浏览器)会自动向服务端发送消息,包含: Sec-WebSocket-Key: iyRe1KMHi4S4QXzcoboMmw==
               - 服务端接收之后,会对于该数据进行加密:
                  base64(sha1(swk + magic_string))
                  
               - 构造响应头:
                  HTTP/1.1 101 Switching Protocols\r\n
                  Upgrade:websocket\r\n
                  Connection: Upgrade\r\n
                  Sec-WebSocket-Accept: 加密后的值\r\n
                  WebSocket-Location: ws://127.0.0.1:8002\r\n\r\n
         
               - 发给客户端(浏览器)
            - 建立:双工通道,接下来就可以进行收发数据
               - 发送的数据是加密,解密,根据payload_len的值进行处理:
                  - payload_len <=125
                  - payload_len ==126
                  - payload_len ==127
               - 获取内容:
                  - mask_key 
                  - 数据 
                  根据mask_key和数据进行位运算,就可以把值解析出来。
                  
         
         面试:
            a. 什么是websocket?
               websocket是给浏览器新建一套协议。协议规定:浏览器和服务端连接之后不断开,以此可以完成:服务端向客户端主动推送消息。
               websocket协议额外做的一些前天操作:
                  - 握手,连接前进行校验
                  - 发送数据加密
               
            b. websocket本质
               - socket
               - 握手,魔法字符串+加密 
               - 加密,payload_len=127/126/<=125 -> mask key 
         
         
         
      在项目中使用:
         - django: channel
         - flask:  gevent-websocket 
         - tornado: 内置