03-Python 框架 Flask、Django、DRF

Django、flask、tornado 框架的比较 ★★★★★

  • Django:大而全的框架。它的内部组件比较多,如 ORM、Admin、中间件、Form、ModelForm、Session、缓存、信号、CSRF 等,功能也都很完善。

  • flask:微型框架,内部组件就比较少了,但是有很多第三方组件来扩展它,比如 wtform(与 Django 的 modelform 类似,表单验证)、flask-sqlalchemy(操作数据库的)、flask-session、flask-migrate、flask-script、blinker 等,可扩展强,第三方组件丰富。所以对他本身来说有那种短小精悍的感觉。

  • tornado:异步非阻塞。是一个轻量级的 Web 框架,异步非阻塞+内置 WebSocket 功能。目标是通过一个线程处理N个并发请求(处理IO)。内部组件:内部自己实现 socket、路由系统、视图、模板、cookie、csrf。

Django 和 flask 对比:

  • 他们都没有写 socket,所以他们都是利用第三方模块 wsgi,但是内部使用的 wsgi 也是有些不同的:Django 使用 wsgiref,而 flask 使用 werkzeug wsgi。

  • 他们的请求管理不太一样:Django 是通过将请求封装成 request 对象,再通过参数传递,而 flask 是通过上下文管理机制。

WSGI、uwsgi 和 uWSGI 的区别 ★★★

  • WSGI:全称 Web Server Gateway Interface(Web服务器网关接口),是一种通用的接口标准或者接口协议。定义了 Web 服务器如何与 Python 应用程序进行交互,使得用 Python 写的 Web 应用程序可以和 Web 服务器对接起来。

    • 以下模块实现了 WSGI 协议:wsgiref, werkzurg, uWSGI
    • 这几个模块本质:编写 socket 服务端,用于监听请求,当有请求到来,则将请求数据进行封装,然后交给 web 框架处理。
  • uwsgi:同 WSGI 一样是一种通信协议,它是一个二进制协议,uwsgi 协议是一个 uWSGI服务器 自有的协议,它用于定义传输信息的类型。

  • uWSGI:uWSGI 是一种 web 服务器,是实现了 uwsgi 和 WSGI 两种协议的 Web 服务器,负责响应 Python 的 web 请求。

Django

简述 MVC 和 MTV ★★★★★

  • MVC:model(数据库)、view(模块)、controller(视图,控制)
  • MTV:model(数据库)、tempalte(视图)、view(控制)

Django 请求的生命周期 ★★★★★

用户请求进来先走到 wsgiref --> 然后将请求交给 Django 的中间件 --> 穿过 Django 中间件(方法是process_request) --> 路由匹配 --> 路由匹配成功之后就执行相应的视图函数 --> 在视图函数中可以调用 ORM 做数据库操作 --> 再从模板路径将模板拿到 --> 然后在后台进行模板渲染 --> 模板渲染完成之后就变成一个字符串 --> 再把这个字符串经过所有中间件(方法:process_response) --> 最后通过 wsgiref 封装返回给用户

列举 Django 的内置组件 ★★★

form 组件

  • 对用户请求的数据进行校验
  • 生成HTML标签

信号

Django 的信号其实就是 Django 内部为开发者预留的一些自定制功能的钩子。

只要在某个信号中注册了函数,那么 Django 内部执行的过程中就会自动触发注册在信号中的函数。如

  • pre_init: Django 的 modal 执行其构造方法前,自动触发。
  • post_init:Django 的 modal 执行其构造方法后,自动触发。
  • pre_save: Django 的 modal 对象保存前,自动触发。
  • post_save:Django 的 modal 对象保存后,自动触发。

场景:在数据库某些表中添加数据时,可以进行日志记录。

CSRF

目标:防止用户直接向服务端发起 POST 请求。对所有的 POST 请求做验证。

方案:发送 GET 请求时,服务器将 token 保存到 cookie、Form 表单中(隐藏的 input 标签),以后浏览器再发送请求时需要携带过来才行。

ContentType

ContentType 是 Django 的一个组件(app),为我们找到 Django 程序中所有 app 中的所有表并添加到记录中。

可以使用他再加上表中的两个字段实现:一张表和 N 张表创建 FK 关系。 - 字段:表名称 - 字段:数据行ID。

中间件

对所有的请求进行批量处理,在视图函数执行前后进行自定义操作。

应用:用户登录校验。

问题:为甚么不使用装饰器?

答案:如果不使用中间件,就需要给每个视图函数添加装饰器,太繁琐。

session

cookie 与 session 区别:

cookie 是保存在浏览器端的键值对,而 session 是保存的服务器端的键值对,但是依赖 cookie。(也可以不依赖 cookie,可以放在 url,或请求头但是 cookie 比较方便)

以登录为例,cookie 为通过登录成功后,设置明文的键值对,并将键值对发送客户端存,明文信息可能存在泄漏,不安全;session 则是生成随机字符串,发给用户,并写到浏览器的 cookie 中,同时服务器自己也会保存一份。

在登录验证时,cookie 验证:根据浏览器发送请求时附带的 cookie 的键值对进行判断,如果存在,则验证通过;session 验证:在请求用户的 cookie 中获取随机字符串,根据随机字符串在 session 中获取其对应的值进行验证。

cors 跨域(场景:前后端分离时,本地测试开发时使用)

首先说域的概念,协议(http/https)+ 域名或IP地址 + 端口号 共同构成了域,三者有一个不同,则为不同的域。
如果网站之间存在跨域,浏览器因为同源策略的限制,而决绝解析返回的数据。
解决:在服务端的响应数据中加上对应的响应头(Django 在中间件 process_response)。

缓存

常用的数据放在缓存里面,就不用走视图函数,请求进来通过所有的 process_request 之后,会到缓存里面查数据,有就直接拿,没有就走视图函数,注意:

  1. 执行完所有的 process_request 才去缓存取数据。
  2. 执行完所有的 process_response 才将数据放到缓存。

关于缓存问题:

  1. 为什么放在最后一个 process_request 才去缓存。
    因为需要验证完用户的请求,才能返回数据。

  2. 什么时候将数据放到缓存中。
    第一次走中间件,缓存没有数据,会走视图函数,取数据库里面取数据,当走完 process_response 才将数据放到缓存里,
    因为走 process_response 的时候可能给我们的响应加处理。

  3. 为什么使用缓存。
    将常用且不太频繁修改的数据放入缓存。以后用户再来访问,先去缓存查看是否存在,如果有就返回,否则,去数据库中获取并返回给用户(再加入到缓存,以便下次访问)。

Django 中间件的5个方法以及应用场景 ★★★★★

1
2
3
4
5
process_request(self,request)                       request 先执行注册的 process_request,通过执行注册的 process_view
process_view(self, request, callback, callback_args, callback_kwargs) 先执行注册的 process_view,再执行 view
process_template_response(self,request,response) 当视图函数的返回值对象中有 render 方法时,该方法才会被调用
process_exception(self, request, exception)
process_response(self, request, response) 返回时执行注册的 process_response

简述什么是 FBV 和 CBV

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# FBV 的路由和视图是基于函数的,FBV 写法
# urls.py
url(r'^login/$',views.login, name="login"),

# views.py
def login(request):
if request.method == "POST":
print(request.POST)

return render(request,"login.html")

# HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>登录页面</title>
</head>
<body>
<form action="{% url 'login' %}" method="post" enctype="multipart/form-data">
<input type="text" name="user2">
<input type="file" name="file">
<input type="submit" value="提交">
</form>
</body>
</html>

# CBV 的路由和视图是基于类的
# urls.py
url(r'^login/$',views.Login.as_view(), name="login"),

# views.py
from django.views import View
class Login(View): # 类首字母大写
def get(self,request):
return render(request,"login.html")
def post(self,request):
print(request.POST)
return HttpResponse("OK")


class IndexView(View):
# 如果是crsf相关,必须放在此处
def dispach(self,request):
# 通过反射执行post/get

@method_decoretor(装饰器函数)
def get(self,request):
pass

def post(self,request):
pass

# 路由:
IndexView.as_view()

FBV 与 CBV 的区别:

没什么区别,因为他们的本质都是函数。CBV 的 .as_view() 返回的 view 函数,view 函数中调用类的 dispatch 方法,在 dispatch 方法中通过反射执行 get/post/delete/put 等方法。

非要说区别的话:CBV 比较简洁,GET/POST 等业务功能分别放在不同 get/post 函数中。FBV 自己做判断进行区分。

Django 的 request 对象是在什么时候创建的 ★★★

当 request 请求到达 Django 服务器时, Django 会建立一个包含请求元数据的 HttpRequest 对象。当 Django 加载对应的视图时, HttpRequest 对象将作为视图函数的第一个参数传递给视图。当执行完视图的代码之后,每个视图会返回一个 HttpResponse 对象。

具体代码是:请求走到 WSGIHandler 类的时候,执行 cell 方法,将 environ 封装成了 request。

1
2
class WSGIHandler(base.BaseHandler):
request = self.request_class(environ)

如何给 CBV 的程序添加装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 方式一:
from django.views import View
from django.utils.decorators import method_decorator # ---> 需要引入memethod_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 # ---> 需要引入 csrf_exempt


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('...')

ORM

列举 Django ORM 中的方法(QuerySet对象的所有方法) ★★★★★

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
返回QuerySet对象的方法有:
all()
filter() # 满足filter条件的对象的集合,其结果的表现形式是列表,单独更具体的获取其中的对象时可以用下标来操作。类似[object1, object2,object3, object4,object5, object6...])表格就映射成一个类,里面的每一行记录就是一个对象,get方法就是获取其中的一个元素,获取的是满足条件的某一个个体。
exclude()
order_by()
reverse()
distinct()
特殊的QuerySet
values() # 返回一个可迭代的字典序列
values_list() # 返回一个可迭代的元组序列
返回具体对象的:
get() # 返回的是一个对象。结果是字典形式,不存在则报错,存在多个也报错
first() # 如果为空时,使用 first() 得到一个 None 对象
last() # 如果为空时,使用 last() 得到一个 None 对象
返回布尔值的方法有:
exists()
返回数字的方法有:
count()

only 和 defer 的区别?

  • only: 仅取某列数据。
  • defer:排除某列数据。
1
2
3
4
5
6
7
8
9
10
11
12
def defer(self, *fields):
# 映射中排除某列数据
models.UserInfo.objects.defer('username','id')
# 或
models.UserInfo.objects.filter(...).defer('username','id')


def only(self, *fields):
# 仅取某个表中的数据
models.UserInfo.objects.only('username','id')
# 或
models.UserInfo.objects.filter(...).only('username','id')

他俩都用于连表查询,减少SQL查询次数。

  • select_related 主要针一对一和多对一关系进行优化,通过多表 join 关联查询,一次性获得所有数据,存放在内存中,但如果关联的表太多,会严重影响数据库性能。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def index(request):
    obj = Book.objects.all().select_related("publisher")
    return render(request, "index.html", locals())

    # 举例
    def select_related(self, *fields)
    # 性能相关:表之间进行join连表操作,一次性获取关联的数据。
    model.tb.objects.all().select_related()
    model.tb.objects.all().select_related('外键字段')
    model.tb.objects.all().select_related('外键字段__外键字段')
  • prefetch_related 是通过分表,先获取各个表的数据,存放在内存中,然后通过 Python 处理他们之间的关联。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def index(request):
    obj = Book.objects.all().prefetch_related("publisher")
    return render(request, "index.html", locals())

    def prefetch_related(self, *lookups)
    # 性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
    # 获取所有用户表
    # 获取用户类型表where id in (用户表中的查到的所有用户ID)
    models.UserInfo.objects.prefetch_related('外键字段')

    from django.db.models import Count, Case, When, IntegerField
    Article.objects.annotate(numviews=Count(Case(When(readership__what_time__lt=treshold, then=1), output_field=CharField(),)))

    students = Student.objects.all().annotate(num_excused_absences=models.Sum(models.Case(models.When(absence__type='Excused', then=1),default=0,output_field=models.IntegerField())))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 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_related()
# 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)

filter 和 exclude 的区别?

1
2
3
4
5
6
7
8
9
def filter(self, *args, **kwargs)
# 条件查询(符合条件)
# 查出符合条件
# 条件可以是:参数,字典,Q

def exclude(self, *args, **kwargs)
# 条件查询(排除条件)
# 排除不想要的
# 条件可以是:参数,字典,Q

列举 Django ORM 中三种能写 SQL 语句的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 原生SQL --->  connection
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(..)


# 靠近原生SQL-->extra\raw
# extra
def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
# 构造额外的查询条件或者映射,如:子查询
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
def raw(self, raw_query, params=None, translations=None, using=None):
# 执行原生SQL
models.UserInfo.objects.raw('select * from userinfo')
# 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
models.UserInfo.objects.raw('select id as nid,name as title 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)
# 指定数据库
models.UserInfo.objects.raw('select * from userinfo', using="default")

Django ORM 中如何设置读写分离?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 方式一:手动使用queryset的using方法
from django.shortcuts import render,HttpResponse
from app01 import models


def index(request):
models.UserType.objects.using('db1').create(title='普通用户')
# 手动指定去某个数据库取数据
result = models.UserType.objects.all().using('db1')
print(result)

return HttpResponse('...')


# 方式二:写配置文件
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'

# 再写到配置
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, 'db.sqlite3'),
}
}
DATABASE_ROUTERS = ['db_router.Router1',]

F 和 Q 的作用 ★★★

F():主要用来获取原数据进行计算。Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。

F查询专门对对象中某列值的操作,不可使用 __ 双下划线!

修改操作也可以使用F函数,比如将每件商品的价格都在原价格的基础上增加10。

1
2
3
4
from django.db.models import F
from app01.models import Goods

Goods.objects.update(price=F("price")+10) # 对于goods表中每件商品的价格都在原价格的基础上增加10

Q:用来进行复杂查询,Q查询可以组合使用 &| 操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象,Q对象可以用 ~ 操作符放在前面表示否定,也可允许否定与不否定形式的组合。

Q对象可以与关键字参数查询一起使用,不过一定要把Q对象放在关键字参数查询的前面。

  • Q(条件1) | Q(条件2):或
  • Q(条件1) & Q(条件2):且
  • Q(条件1) & ~Q(条件2):非

valuesvalues_list 的区别?

1
2
3
4
5
def values(self, *fields):
# 获取每行数据为字典格式

def values_list(self, *fields, **kwargs):
# 获取每行数据为元祖

如何使用 Django ORM 批量创建数据?

1
2
3
4
5
6
7
8
def bulk_create(self, objs, batch_size=None):
# 批量插入
# batch_size 表示一次插入的个数
objs = [
models.DDD(name='r11'),
models.DDD(name='r22')
]
models.DDD.objects.bulk_create(objs, 10)

Django 的 Form 和 ModelForm 的作用?

  • 作用:

    • 对用户请求数据格式进行校验。
    • 自动生成HTML标签。
  • 区别:

    • Form,字段需要自己手写。
      1
      2
      3
      4
      5
      class Form(Form):
      xx = fields.CharField(.)
      xx = fields.CharField(.)
      xx = fields.CharField(.)
      xx = fields.CharField(.)
    • ModelForm,可以通过 Meta 进行定义
      1
      2
      3
      4
      class MForm(ModelForm):
      class Meta:
      fields = "__all__"
      model = UserInfo
  • 应用:只要是客户端向服务端发送表单数据时,都可以进行使用,如:用户登录注册。

Django 的 Form 组件中,如果字段中包含choices参数,请使用两种方式实现数据源实时更新。

原因:choice 的数据如果从数据库获取可能会造成数据无法实时更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 方式一:重写构造方法,在构造方法中重新去数据库获取值
class UserForm(Form):
name = fields.CharField(label='用户名',max_length=32)
email = fields.EmailField(label='邮箱')
ut_id = fields.ChoiceField(
# choices=[(1,'普通用户'),(2,'IP用户')]
choices=[]
)

def __init__(self,*args,**kwargs):
super(UserForm,self).__init__(*args,**kwargs)
self.fields['ut_id'].choices = models.UserType.objects.all().values_list('id','title')


# 方式二: 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

Django 的 Model 中的 ForeignKey 字段中的 on_delete 参数有什么作用

在 Django2.0 后,定义外键和一对一关系的时候需要加 on_delete 选项,此参数为了避免两个表里的数据不一致问题,不然会报错:

TypeError: __init__() missing 1 required positional argument: 'on_delete'

举例说明:

1
2
3
4
5
6
user=models.OneToOneField(User)
owner=models.ForeignKey(UserProfile)

# 需要改成:
user=models.OneToOneField(User,on_delete=models.CASCADE) # 在老版本这个参数(models.CASCADE)是默认值
owner=models.ForeignKey(UserProfile,on_delete=models.CASCADE) # 在老版本这个参数(models.CASCADE)是默认值

on_delete 有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五个可选择的值:

  • CASCADE:此值设置,是级联删除。一般情况下使用CASCADE就可以了。
  • PROTECT:此值设置,是会报完整性错误。
  • SET_NULL:此值设置,会把外键设置为null,前提是允许为null。
  • SET_DEFAULT:此值设置,会把设置为外键的默认值。
  • SET():此值设置,会调用外面的值,可以是一个函数。

解释 ORM 中 db first 和 code first 的含义?

Django 中如何根据数据库表生成 model 中的类?

  1. 修改 setting 文件,在 setting 里面设置要连接的数据库类型和名称、地址。
  2. 运行 python manage.py inspectdb 可以自动生成models模型文件。
  3. 创建一个app执行 python manage.py inspectdb > app/models.py 命令。

使用 ORM 和原生 sql 的优缺点

SQL:

  • 优点:执行速度快。
  • 缺点:编写复杂,开发效率不高。

ORM:

  • 优点:让用户不再写SQL语句,提高开发效率,可以很方便地引入数据缓存之类的附加功能。
  • 缺点:在处理多表联查、where 条件复杂查询时,ORM 的语法会变得复杂,没有原生 SQL 速度快。

Django 中如何实现 ORM 表中添加数据时创建一条日志记录

给信号注册函数,使用 Django 的信号机制,可以在添加、删除数据前后设置日志记录。

1
2
3
4
5
6
pre_init        # Django 中的 model 对象执行其构造方法前,自动触发
post_init # Django 中的 model 对象执行其构造方法后,自动触发
pre_save # Django 中的 model 对象保存前,自动触发
post_save # Django 中的 model 对象保存后,自动触发
pre_delete # Django 中的 model 对象删除前,自动触发
post_delete # Django 中的 model 对象删除后,自动触发

Django 中 csrf 的实现机制 ★★★★★

目的:防止用户直接向服务端发起 POST 请求。

  1. 配置文件中开启 CSRF 中间件,然后在所有的 POST 表单模板中,加一个 {\%csrf_token%\} 标签,它的功能其实是给 form 增加一个隐藏的 input 标签,<input type="hidden" name="csrfmiddlewaretoken" value="">
    而这个 csrf_token = cookie.csrftoken,在渲染模板时 context 中有 context['csrf_token'] = request.COOKIES['csrftoken']

  2. 在用户访问 Django 的站点时,Django 会渲染该模板,并在页面中生成一个 csrftoken 口令,这个值是在服务器端随机生成的,每一次提交表单都会生成不同的值。

  3. 在通过表单发送 POST 到服务器时,表单中包含了上面隐藏了 crsrmiddlewaretoken 这个 input 项,服务端收到后,Django 会验证这个请求的 cookie 里的 csrftoken 字段的值和提交的表单里的 csrfmiddlewaretoken 字段的值是否一样。
    如果一样,则表明这是一个合法的请求,否则,这个请求可能是来自于别人的 csrf 攻击,返回 403 Forbidden。

  4. 如果使用 ajax 发送 POST 请求到服务器时,要求 header 增加一个x-csrftoken,其值为 cookie 里的 csrftoken 的值,服务湍收到后 Django 会验证这个请求的 cookie 里的 csrftoken 字段与 ajax post消息头中的 x-csrftoken 是否相同,如果相同,则表明是一个合法的请求。

在中间件的 process_view 方法中进行校验

基于 Django 使用 ajax 发送 post 请求时,都可以使用哪种方法携带 csrf_token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# 方式一: 给每个ajax都加上上请求头
function Do1(){
$.ajax({
url:"/index/",
data:{id:1},
type:'POST',
headers:{csrfmiddlewaretoken:'{{ csrf_token }}',name:'alex'}
success:function(data){
console.log(data);
}
});
}

# 方式二:需要先下载jQuery-cookie,才能去cookie中获取token
function Do1(){
$.ajax({
url:"/index/",
data:{id:1},
type:'POST',
headers:{
'X-CSRFToken':$.cookie('csrftoken') // 去cookie中获取
},
success:function(data){
console.log(data);
}
});
}

# 方式三:搞个函数ajaxSetup,当有多的ajax请求,即会执行这个函数
$.ajaxSetup({
beforeSend:function (xhr,settings) {
xhr.setRequestHeader("X-CSRFToken",$.cookie('csrftoken'))
}
});

# 函数版本
<body>
<input type="button" onclick="Do1();" value="Do it"/>
<input type="button" onclick="Do2();" value="Do it"/>
<input type="button" onclick="Do3();" value="Do it"/>

<script src="/static/jquery-3.3.1.min.js"></script>
<script src="/static/jquery.cookie.js"></script>
<script>
$.ajaxSetup({
beforeSend: function(xhr, settings) {
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
}
});

function Do1(){
$.ajax({
url:"/index/",
data:{id:1},
type:'POST',
success:function(data){
console.log(data);
}
});
}

function Do2(){
$.ajax({
url:"/index/",
data:{id:1},
type:'POST',
success:function(data){
console.log(data);
}
});
}

function Do3(){
$.ajax({
url:"/index/",
data:{id:1},
type:'POST',
success:function(data){
console.log(data);
}
});
}
</script>
</body>

Django 如何实现 websocket

Django 可以通过 channel 实现 websocket。

Django 缓存

Django 中提供了 6 种缓存方式:

  • 开发调试(不加缓存)
  • 内存
  • 文件
  • 数据库
  • Memcache 缓存(python-memcached模块)
  • Memcache 缓存(pylibmc模块)
  • 安装第三方组件支持 Redis:django-redis组件

设置缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 全站缓存(中间件)
MIDDLEWARE_CLASSES = (
'django.middleware.cache.UpdateCacheMiddleware', # 第一
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware', # 最后
)

# 视图缓存
from django.views.decorators.cache import cache_page
import time

@cache_page(15) # 超时时间为15秒
def index(request):
t=time.time() # 获取当前时间
return render(request,"index.html",locals())

# 模板缓存
{xxx% load cache %xxx} # 因为前端显示冲突的问题,需要把 xxx 删掉
<h3 style="color: green">不缓存:-----{{ t }}</h3>
{xxx% cache 2 'name' %xxx} # 存的key # 因为前端显示的问题,需要把 xxx 删掉
<h3>缓存:-----:{{ t }}</h3>
{xxx% endcache %xxx} # 因为前端显示冲突的问题,需要把 xxx 删掉

Django 的缓存能使用 Redis 吗?如果可以的话,如何配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 安装
# pip install django-redis
# apt-get install redis-serv

# 在 setting 添加配置文件
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache", # 缓存类型
"LOCATION": "127.0.0.1:6379", # ip端口
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient", #
"CONNECTION_POOL_KWARGS": {"max_connections": 100} # 连接池最大连接数
# "PASSWORD": "密码",
}
}
}


# 使用
from django.shortcuts import render,HttpResponse
from django_redis import get_redis_connection

def index(request):
# 根据名字去连接池中获取连接
conn = get_redis_connection("default")
conn.hset('n1','k1','v1') # 存数据
return HttpResponse('...')

Django 路由系统中 name 的作用

路由系统中 name 的作用:反向解析路由字符串。

  • 路由:url(r'^home', views.home, name='home')
  • 在模板中使用:{% url 'home' %}
  • 在视图中使用:reverse("home")

Django 的模板中 filtersimple_tag 的区别

  • filter: 类似管道,只能接受两个参数,第一个参数是 | 前的数据。
  • simple_tag: 类似函数。
  1. 模板继承:{% extends 'layouts.html' %}
  2. 自定义方法
    • filter:只能传递两个参数,可以在if、for语句中使用。
    • simple_tag:可以无线传参,不能在if for中使用。
    • inclusion_tags:可以使用模板和后端数据。
  3. 防 xss 攻击: |safemark_safe

django-debug-toolbar的作用?

  1. 查看访问的速度、数据库的行为、cache命中等信息。
  2. 尤其在 Mysql 访问等的分析上大有用处(sql查询速度)。

Django 中如何实现单元测试

对于每一个测试方法都会将 setUp()tearDown() 方法执行一遍。

会单独新建一个测试数据库来进行数据库的操作方面的测试,默认在测试完成后销毁。

在测试方法中对数据库进行增删操作,最后都会被清除。也就是说,在 test_add 中插入的数据,在 test_add 测试结束后插入的数据会被清除。

Django 单元测试时为了模拟生产环境,会修改 settings 中的变量,例如, 把 DEBUG 变量修改为 True, 把 ALLOWED_HOSTS 修改为 [*]

Django 的 contenttype 组件的作用

contenttype 是 Django 的一个组件(app),它可以将 Django 下所有 app 下的表记录下来。

可以使用他再加上表中的两个字段,实现一张表和 N 张表动态创建 FK 关系。

  • 字段:表名称
  • 字段:数据行ID
  • 应用:一个策略表,同时关联不同的告警事件表

RESTful

谈谈你对 RESTful 规范的认识

https://blog.csdn.net/fenglepeng/article/details/104698864

RESTful 其实就是一套编写接口的’协议’,规定如何编写以及如何设置返回值、状态码等信息。

  • 用 RESTful: 给用户一个 url,根据 method 不同在后端做不同的处理。比如:post创建数据、get获取数据、put和patch修改数据、delete删除数据。
  • 不用 RESTful: 给调用者很多 url,每个 url 代表一个功能,比如:add_user/delte_user/edit_user/

规范:

  • 使用HTTPS协议。
  • 域名体现是API。
  • 体现版本。’版本’来控制让程序有多个版本共存的情况,版本可以放在 url、请求头(accept/自定义)、GET参数。
  • 路径,网络上任何东西都是资源,均使用名词表示(可复数)面向资源编程,RESTful 也可以称为“面向资源编程”。
  • 根据 method 不同,进行不同操作。
    • GET :从服务器取出资源(一项或多项)
    • POST :在服务器新建一个资源
    • PUT :在服务器更新资源(客户端提供改变后的完整资源)
    • PATCH :在服务器更新资源(客户端提供改变的属性)
    • DELETE :从服务器删除资源
  • 通过在 url 上传参的形式传递搜索条件
  • 响应式设置状态码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
    201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
    202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
    204 NO CONTENT - [DELETE]:用户删除数据成功。
    400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
    401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
    403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
    404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
    406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
    410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
    422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
    500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功
  • 状态码是 4xx 时,应返回错误信息,error 当做key。
  • 针对不同操作,服务器向用户返回的结果应该符合以下规范
    1
    2
    3
    4
    5
    GET /collection:返回资源对象的列表(数组)
    GET /collection/resource:返回单个资源对象;获取单条数据
    POST /collection:返回新生成的资源对象;返回新增的数据
    PUT /collection/resource:返回完整的资源对象;更新
    PATCH /collection/resource:返回完整的资源对象;局部更新
  • Hypermedia API: RESTful API 最好做到 Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。

接口的幂等性是什么意思

接口的幂等性指的是对于同一接口的多次请求,其结果应保持一致,即不会因为请求的多次执行而产生不同的结果或副作用。

一个幂等操作满足以下条件:

  1. 多次执行相同操作,产生的效果与执行一次相同操作的效果相同。
  2. 重复执行相同请求的结果与第一次执行该请求的结果相同。
  • GET # 多次也是获取结果对资源都不会造成影响,幂等。
  • POST # 多次请求会多次新增,不是幂等。
  • PUT # 如果是绝对值更新,是幂等的。如果是增量的更新,不是幂等。
  • PATCH # 如果是绝对值更新,是幂等的。如果是增量的更新,不是幂等。
  • DELTE # 根据唯一值删除数据,是幂等。

DRF(Django rest framework)

为什么要使用 Django rest framework 框架

在编写接口时可以不使用 Django rest framework 框架。

  • 不使用:也可以做,可以用 Django 的 CBV 来实现,开发者编写的代码会更多一些。
  • 使用:内部帮助我们提供了很多方便的组件,我们通过配置就可以完成相应操作。

Django rest framework 框架中都有那些组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#- 路由,自动帮助开发者快速为一个视图创建4个url
www.oldboyedu.com/api/v1/student/$
www.oldboyedu.com/api/v1/student(?P<format>\w+)$
www.oldboyedu.com/api/v1/student/(?P<pk>\d+)/$
www.oldboyedu.com/api/v1/student/(?P<pk>\d+)(?P<format>\w+)$

#- 版本处理
- 问题:版本都可以放在那里?
- url
- GET
- 请求头

#- 认证
- 问题:认证流程?

#- 权限
- 权限是否可以放在中间件中?以及为什么?

#- 访问频率的控制
匿名用户可以真正的防止?无法做到真正的访问频率控制,只能把小白拒之门外。
如果要封IP,使用防火墙来做。
登录用户可以通过用户名作为唯一标示进行控制,如果有人注册很多账号,则无法防止。

#- 视图

#- 解析器 ,获取用户请求数据request.data,根据Content-Type请求头对请求体中的数据格式进行处理。

#- 分页

#- 序列化,可以做用户请求数据校验+queryset对象的序列化
- 序列化
- source
- 定义方法
- 请求数据格式校验
#- 渲染器

Django rest framework 框架中的视图都可以继承哪些类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
a. 继承APIView(最原始)但定制性比较强
这个类属于rest framework中的顶层类,内部帮助我们实现了只是基本功能:认证、权限、频率控制,但凡是数据库、分页等操作都需要手动去完成,比较原始。
class GenericAPIView(APIView)
def post(...):
pass

b. 继承GenericViewSetViewSetMixin,generics.GenericAPIView
首先他的路由发生变化:如果继承它之后,路由中的as_view需要填写对应关系
在内部也帮助我们提供了一些方便的方法:
get_queryset
get_object
get_serializer
get_serializer_class
get_serializer_context
filter_queryset
注意:要设置queryset字段,否则会抛出断言的异常。

代码
只提供增加功能,只继承GenericViewSet

class TestView(GenericViewSet):
serialazer_class = xxx
def creat(self,*args,**kwargs):
pass # 获取数据并对数据

c. 继承 modelviewset --> 快速快发
-ModelViewSet(增删改查全有+数据库操作)
-mixins.CreateModelMixin(只有增),GenericViewSet
-mixins.CreateModelMixin,DestroyModelMixin,GenericViewSet
对数据库和分页等操作不用我们在编写,只需要继承相关类即可。

示例:只提供增加功能
class TestView(mixins.CreateModelMixin,GenericViewSet):
serializer_class = XXXXXXX
***
modelviewset --> 快速开发,复杂点的genericview、apiview

简述 Django rest framework 框架的认证流程

源码流程:请求进来先走 dispatch 方法,然后封装的 request 对象会执行 user 方法,由 user 触发 authenticators 认证流程。

如何编写类并实现 authenticators?

认证需要编写一个类,类里面有一个 authenticators 方法,我们可以自定义这个方法,可以定制3类返回值。

  • (user,auth),认证成功
  • None, 匿名用户
  • 异常,认证失败

Django rest framework 如何实现的用户访问频率控制 ★★★

1
2
3
4
5
6
7
8
# 对匿名用户,根据用户IP或代理IP作为标识进行记录,为每个用户在redis中建一个列表
{
throttle_1.1.1.1:[1526868876.497521,152686885.497521...],
throttle_1.1.1.2:[1526868876.497521,152686885.497521...],
throttle_1.1.1.3:[1526868876.497521,152686885.497521...],
}

每个用户再来访问时,需先去记录中剔除过期记录,再根据列表的长度判断是否可以继续访问。

如何封IP:在防火墙中进行设置

Flask

Flask 框架的优势

Flask 自由、灵活、可扩展性强、透明可控、第三方库的选择面广、开发时可以结合最流行最强大的 Python 库。

Flask blueprint(蓝图)的作用

Flask blueprint 把实现不同功能的 module 分开。也就是把一个大的 App 分割成各自实现不同功能的 module。

在一个 blueprint 中可以调用另一个 blueprint 的视图函数, 但要加相应的 blueprint 名。

列举使用的 Flask 第三方组件

  • Flask 组件
    • flask-session session 放在 redis
    • flask-SQLAlchemy 同 Django 的 ORM 操作
    • flask-migrate 数据库迁移
    • flask-script 自定义命令
    • blinker 信号-触发信号
  • 第三方组件
    • Wtforms 快速创建前端标签、文本校验,和 Django 的 ModelForm 作用类似。
    • dbutile 创建数据库连接池
    • gevnet-websocket 实现 websocket
  • 自定义 Flask 组件
    • 自定义 auth 认证,参考 flask-login 组件

Flask 框架依赖组件

  • 依赖 jinja2 模板引擎。
  • 依赖 werkzurg。

在 Flask 中实现 WebSocket 需要什么组件?

gevent-websocket

简述 Flask 上下文管理流程 ★★★

简单来说,Falsk 上下文管理可以分为三个阶段:

  1. 请求进来时:将请求相关的数据放入上下文管理中。
  2. 在视图函数中:要去上下文管理中取值。
  3. 请求响应:要将上下文管理中的数据清除。

详细点来说:

  1. 请求刚进来:
    将 request、session 封装在 RequestContext 类中,app、g 封装在 AppContext 类中。
    并通过 LocalStack 将 RequestContext 和 AppContext 放入 Local 类中。

  2. 视图函数中:
    通过 localproxy —> 偏函数 —> localstack —> local 取值。

  3. 请求响应时:
    先执行 save.session(),再各自执行 pop(),将 local 中的数据清除。

Flask 中上下文管理主要涉及到了那些相关的类?并描述类主要作用?

  • RequestContext # 封装进来的请求(赋值给ctx)
  • AppContext # 封装app_ctx
  • LocalStack # 将local对象中的数据维护成一个栈(先进后出)
  • Local # 保存请求上下文对象和app上下文对象

Flask 默认 session 处理机制

不熟的话:记不太清了,应该是……分两个阶段吧。

  • 创建:
    当请求刚进来的时候,会将 requestsession 封装成一个 RequestContext() 对象。
    接下来把这个对象通过 LocalStack() 放入内部的一个 Local() 对象中。
    因为刚开始 Local 的 ctx 中 session 是空的,所以接着执行 open_session,将 cookie 里面的值拿过来,重新赋值到 ctx 中(Local实现对数据隔离,类似 threading.local)

  • 销毁:
    最后返回时执行 save_session() 将 ctx 中的 session 读出来进行序列化,写到 cookie 然后给用户,接着把 ctx pop 掉。

Flask 中的 g 的作用

g 是贯穿于一次请求的全局变量,当请求进来将 gcurrent_app 封装为一个 APPContext 类,再通过 LocalStack 将 Appcontext 放入 Local 中。

取值时通过偏函数在 LocalStack、local 中取值;响应时将 local 中的 g 删除。

在 Flask 中,g 对象是专门用来存储用户数据的,它是 global 的缩写,g 是全局变量,在整个 request 生命周期内生效。

g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的 thread id 区别,像数据库配置这样重要的信息挂载在 app 对象上,一些用户相关的数据,就可以挂载在 g 对象上,这样就不需要在函数里一层层传递。

为什么 Flask 把 Local 对象中的的值 stack 维护成一个列表

因为通过维护成列表,可以实现一个栈的数据结构,进栈出栈时只取一个数据,巧妙的简化了问题。

还有,在多 app 应用时,可以实现数据隔离;列表里不会加数据,而是会生成一个新的列表。

local 是一个字典,字典里key(stack)是唯一标识,value是一个列表。

Flask 中多 app 应用是怎么完成

请求进来时,可以根据 URL 的不同,交给不同的 APP 处理。蓝图也可以实现。

1
2
3
4
#app1 = Flask('app01')
#app2 = Flask('app02')
#@app1.route('/index')
#@app2.route('/index2')

源码中在 DispatcherMiddleware 类里调用 app2.__call__,原理其实就是 URL 分割,然后将请求分发给指定的 app。之后 app 也按单 app 的流程走。就是从 app.__call__ 走。

解释 Flask 框架中的 Local 对象和 threading.local 对象的区别

  • Local对象:Local 对象是根据 threading.local 做的,为每个 request 开辟一块空间进行数据存储。

  • threading.local:为每个线程开辟一块空间进行数据存储(数据隔离)。

问题:自己通过字典创建一个类似于 threading.local 的东西。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
storage = {
4740: {val: 0},
4732: {val: 1},
4731: {val: 3},
}


class Local(object):
def __init__(self):
object.__setattr__(self, 'storage', {})
def __setattr__(self, k, v):
ident = get_ident()
if ident in self.storage:
self.storage[ident][k] = v
else:
self.storage[ident] = {k: v}
def __getattr__(self, k):
ident = get_ident()
return self.storage[ident][k]
obj = Local()
def task(arg):
obj.val = arg
obj.xxx = arg
print(obj.val)
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()

Flask 中 blinker 是什么?

Flask 中的 blinker 是信号的意思,信号主要是让开发者可是在 Flask 请求过程中定制一些行为。

或者说 Flask 在列表里面预留了几个空列表,在里面存东西。简言之,信号允许某个’发送者’通知’接收者’有事情发生了。

@before_request 有返回值,blinker 没有返回值

1
2
3
4
5
6
7
8
9
10
11
# 10个信号
request_started = _signals.signal('request-started') # 请求到来前执行
request_finished = _signals.signal('request-finished') # 请求结束后执行
before_render_template = _signals.signal('before-render-template') # 模板渲染前执行
template_rendered = _signals.signal('template-rendered') # 模板渲染后执行
got_request_exception = _signals.signal('got-request-exception') # 请求执行出现异常时执行
request_tearing_down = _signals.signal('request-tearing-down') # 请求执行完毕后自动执行(无论成功与否)
appcontext_tearing_down = _signals.signal('appcontext-tearing-down')# 请求上下文执行完毕后自动执行(无论成功与否)
appcontext_pushed = _signals.signal('appcontext-pushed') # 请求app上下文push时执行
appcontext_popped = _signals.signal('appcontext-popped') # 请求上下文pop时执行
message_flashed = _signals.signal('message-flashed') # 调用flask在其中添加数据时,自动触发

SQLAlchemy 中的 sessionscoped_session 的区别?

  • Session:
    由于无法提供线程共享功能,开发时要给每个线程都创建自己的 session,打印 sesion 可知他是 sqlalchemy.orm.session.Session 的对象。

  • scoped_session
    为每个线程都创建一个 session,实现支持线程安全,在整个程序运行的过程当中,只存在唯一的一个 session 对象。
    创建方式:通过本地线程 Threading.Local() session=scoped_session(Session) 创建唯一标识的方法(参考flask请求源码)。

SQLAlchemy 如何执行原生SQL?

1
2
3
4
5
# 使用execute方法直接操作SQL语句(导入create_engin、sessionmaker)
engine=create_engine('mysql://root:*****@127.0.0.1/database?charset=utf8')
DB_Session = sessionmaker(bind=engine)
session = DB_Session()
session.execute('alter table mytablename drop column mycolumn ;')

ORM 的实现原理?

ORM的实现基于一下三点:

  • 映射类:描述数据库表结构。
  • 映射文件:指定数据库表和映射类之间的关系。
  • 数据库配置文件:指定与数据库连接时需要的连接信息(数据库、登录用户名、密码or连接字符串)。

以下 SQLAlchemy 的字段是否正确?如果不正确请更正:

1
2
3
4
5
6
7
8
9
10
11
12
13
from datetime import datetime
from sqlalchemy.ext.declarative
import declarative_base
from sqlalchemy import Column, Integer, String, DateTime

Base = declarative_base()
class UserInfo(Base):
__tablename__ = 'userinfo'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(64), unique=True)
ctime = Column(DateTime, default=datetime.now())

# 不正确:Ctime字段中参数应为 default=datetime.now,now 后面不应该加括号,加了的话,字段不会实时更新。

SQLAchemy 中如何为表设置引擎和字符编码?

  1. 设置引擎编码方式为utf8。
    engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/sqldb01?charset=utf8")

  2. 设置数据库表编码方式为utf8

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class UserType(Base):
    __tablename__ = 'usertype'
    id = Column(Integer, primary_key=True)
    caption = Column(String(50), default='管理员')

    # 添加配置设置编码
    __table_args__ = {
    'mysql_charset':'utf8'
    }

    这样生成的SQL语句就自动设置数据表编码为utf8了,__table_args__ 还可设置存储引擎、外键约束等等信息。

SQLAchemy 中如何设置联合唯一索引

通过 UniqueConstraint 字段来设置联合唯一索引,例如 __table_args=(UniqueConstraint('h_id','username',name='_h_username_uc')) h_id 和 username 组成联合唯一约束。

Tornado

简述 Tornado 框架的特点。

异步非阻塞+websocket

简述 Tornado 框架中 Future 对象的作用?

实现异步非阻塞。

视图函数 yield 一个 futrue 对象,futrue 对象默认:
self._done = False ,请求未完成
self._result = None ,请求完成后返回值,用于传递给回调函数使用。

tornado 就会一直去检测 futrue 对象的 _done 是否已经变成 True。

如果IO请求执行完毕,自动会调用 future 的 set_result 方法:
self._result = result
self._done = True
参考:http://www.cnblogs.com/wupeiqi/p/6536518.html(自定义异步非阻塞web框架)

Tornado 框架中如何编写 WebSocket 程序

Tornado 在 websocket 模块中提供了一个 WebSocketHandler 类。这个类提供了和已连接的客户端通信的 WebSocket 事件和方法的钩子。

当一个新的 WebSocket 连接打开时,open 方法被调用,而 on_message 和 on_close 方法,分别在连接、接收到新的消息和客户端关闭时被调用。

此外,WebSocketHandler 类还提供了 write_message 方法用于向客户端发送消息,close方法用于关闭连接。

### Tornado 中静态文件是如何处理的?


如:<link href="{\{ static_url("commons.css") }\}" rel="stylesheet" />

1
2
3
4
5
6
7
8
9
10
11
# settings.py
settings = {
"static_path": os.path.join(os.path.dirname(__file__), "static"),
# 指定了静态文件的位置在当前目录中的"static"目录下
"cookie_secret": "61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
"login_url": "/login",
"xsrf_cookies": True,
}

经上面配置后
static_url()自动去配置的路径下找'commons.css'文件

Tornado 操作 MySQL 使用的模块

torndb: torndb 是基于 mysqldb 的再封装,所以使用时要先安装 myqldb

Tornado 操作 redis 使用的模块

tornado-redis

简述 Tornado 框架的适用场景

web聊天室,在线投票


03-Python 框架 Flask、Django、DRF
https://flepeng.github.io/interview-20-开发语言类-21-Python-03-Python-框架-Flask、Django、DRF/
作者
Lepeng
发布于
2020年8月8日
许可协议