Django-Part3——视图层

[TOC]

三板斧

HttpResponse

  • HttpResponse:返回字符串类型
  • render:返回html页面,并且在返回给浏览器之前还可以给html文件传值
  • redirect:重定向

视图函数必须要返回一个HttpResponse对象

render

  • 返回html文件
1
2
3
4
5
6
7
8
def test(request):
from django.template import Template, Context
res = Template('<h1>{{ user }}</h1>') # 模板对象
con = Context({'user': {'username': 'jason', 'password': 123}}) # 动态确定模板变量的内容,此处是{'username':'jason', 'password':123}
ret = res.render(con) # 将模板和动态内容组装
print(ret) # <h1>{&#x27;username&#x27;: &#x27;jason&#x27;, &#x27;password&#x27;: 123}</h1>
print(type(ret)) # <class 'django.utils.safestring.SafeString'>
return HttpResponse(ret)

redirect

  • 重定向至任意网站
    return redirect('https://www.mzitu.com/')

  • 跳转本站不需要加https

    return redirect('/home/')

JsonResponse

  • 前后端数据交互需要使用json作为过渡,实现跨语言传输数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
def ab_json(request):
user_dict = {'username': 'jason好帅哦,我好喜欢!', 'password': '123', 'hobby': 'girl'}
l = [111,222,333,444,555]

# import json # 先转成json格式字符串
# json_str = json.dumps(user_dict, ensure_ascii=False) # 禁止内部转码
# return HttpResponse(json_str) # 将该字符串作为参数,返回HttpResponse对象

from django.http import JsonResponse
return JsonResponse(user_dict, json_dumps_params={"ensure_ascii": False})

# 报错:In order to allow non-dict objects to be serialized set the safe parameter to False.
# return JsonResponse(l, safe=False)
  • 指将字典打散成关键字的形式
  • 默认只能序列化字典,序列化其他需要加safe参数

很多时候都可以通过读源码来掌握用法

上传文件与后端操作

使用form表单上传

在 .html 中:

  1. method 必须指定成 post
  2. enctype 必须换成 multipart/formdata

使用requests模块上传

  • Requests 使得上传多部分编码文件变得很简单
1
2
3
4
5
6
7
8
9
10
11
>>> url = 'http://httpbin.org/post'
>>> files = {'file': open('report.xls', 'rb')}
>>> r = requests.post(url, files=files)
>>> r.text
{
...
"files": {
"file": "<censored...binary...data>"
},
...
}
  • 你可以显式地设置文件名,文件类型和请求头
1
2
3
4
5
6
7
8
9
10
11
>>> url = 'http://httpbin.org/post'
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}
>>> r = requests.post(url, files=files)
>>> r.text
{
...
"files": {
"file": "<censored...binary...data>"
},
...
}
  • 也可以发送作为文件来接收的字符串
1
2
3
4
5
6
7
8
9
10
11
>>> url = 'http://httpbin.org/post'
>>> files = {'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')}
>>> r = requests.post(url, files=files)
>>> r.text
{
...
"files": {
"file": "some,data,to,send\\nanother,row,to,send\\n"
},
...
}
  • 把多个文件组织到一个元组的列表中,其中元组结构为 (form_field_name, file_info)
1
2
3
4
5
data = {'ts_id': tsid}
files = [('images', ('1.png', open('/home/1.png', 'rb'), 'image/png')),
('images', ('2.png', open('/home/2.png', 'rb'), 'image/png'))]
r = requests.post(url, data=data, files=files)
print r.text

使用request接收文件

1
2
3
4
5
6
7
8
9
10
def ab_file(request):
if request.method == 'POST':
print(request.POST) # 只能获取普通的键值对数据,文件不行 <QueryDict: {'username': ['']}>
print(request.FILES) # 获取文件数据 <MultiValueDict: {'file': [<InMemoryUploadedFile: 4101021694766.jpg (image/jpeg)>]}>
file_obj = request.FILES.get('file') # 获取文件对象
print(file_obj.name) # 4101021694766.jpg
with open(file_obj.name, 'wb') as f:
for line in file_obj.chunks(): # 推荐加上chunks方法,两者几乎等效
f.write(line)
return render(request,'form.html')
  • 接收保存多个文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from werkzeug.utils import secure_filename

def upload_file(request):
if request.method == 'POST':
uploaded_files = request.FILES.getlist("images") # 获取相同标签
try:
for file_obj in uploaded_files:
file_name = secure_filename(file_obj.name)
handle_uploaded_file(os.path.join(file_name), file_obj)
result_json = {"msg": "success"}
except Exception as e:
result_json = {"msg": str(e)}
return JsonResponse(result_json, safe=False)


def handle_uploaded_file(file_name, file_obj):
try:
with open(file_name, 'wb') as f:
for chunk in file_obj.chunks():
f.write(chunk)
except Exception as e:
raise Exception('save %s failed: %s' % (file_name, str(e)))

request对象方法

  • get请求携带的数据是有大小限制的,而post请求则没有限制。
1
2
3
4
5
def login(request):
# 视图函数必须要接受一个形参request
if request.method == 'POST': # 返回请求方式 并且是全大写的字符串形式 <class 'str'>
return HttpResponse("收到了 宝贝")
return render(request, 'login.html')
  • 对于get请求和post请求应该有不同的处理机制
  • post做特殊处理,而将request放置在if围绕之外,提高可阅读性
  1. request.method:返回请求方式,并且是全大写的字符串形式 <class ‘str’>
  2. request.POST:获取用户post请求提交的普通数据不包含文件
  3. request.POST.get():只获取列表最后一个元素
  4. request.POST.getlist():直接将列表取出
  5. request.GET:获取用户提交的get请求数据
  6. request.GET.get():只获取列表最后一个元素
  7. request.GET.getlist():直接将列表取出
  8. request.FILES.get():用键取出对应的文件,是以字典形式封装好的
  9. request.body:发过来的原生二进制数据
  10. request.path_info:获取url(例如/app01/ab_file/),和request.path是等效的
  11. request.get_full_path():获取完整的url及问号后面的参数

可以使用以下装饰器来指定请求类型:

  1. @require_http_methods([“GET”])
  2. @require_http_methods([“POST”])

FBV与CBV

视图函数既可以是 函数 FBV(function base views),也可以是 类 CBV(class base views)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# FBV
def index(request):
return HttpResponse('index')
# FBV路由
path("login/", views.login)

# CBV
from django.views import View
class MyLogin(View):
def get(self,request):
return render(request, 'form.html')

def post(self,request):
return HttpResponse('post方法')
# CBV路由
url(r'^login/', views.MyLogin.as_view())

CBV源码剖析

  • 首先,不建议自己修改源码
  • 函数名/方法名,加括号执行优先级最高,例如views.MyLogin.as_view()会立刻执行as_view()方法
  • CBV与FBV在路由匹配上本质是一样的,都是路由对应函数内存地址。
  • 在没有实例的情况下使用函数,则只可能使用@staicmethod或者@classmethod
  • 在看python源码的时候,一定要时刻提醒自己面向对象属性方法查找顺序:
    • 先从对象自己找
    • 再去产生对象的类里面找
    • 之后再去父类找
  • 反射 getattr()
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
# 突破口在urls.py
url(r'^login/', views.MyLogin.as_view())
url(r'^login/', views.view)
# CBV与FBV在路由匹配上本质是一样的,都是路由对应函数内存地址
"""
函数名/方法名 加括号执行优先级最高
猜测:
as_view()
要么是被@staicmethod修饰的静态方法
要么是被@classmethod修饰的类方法 正确

@classonlymethod
def as_view(cls, **initkwargs):
pass
"""

@classonlymethod
def as_view(cls, **initkwargs):
"""
cls就是我们自己写的类 MyCBV
Main entry point for a request-response process.
"""
def view(request, *args, **kwargs):
self = cls(**initkwargs) # cls是我们自己写的类
# self = MyLogin(**initkwargs) 产生一个我们自己写的类的对象
return self.dispatch(request, *args, **kwargs)
"""
以后你们会经常需要看源码 但是在看python源码的时候 一定要时刻提醒自己面向对象属性方法查找顺序
先从对象自己找
再去产生对象的类里面找
之后再去父类找
...
总结:看源码只要看到了self点一个东西,一定要问你自己当前这个self到底是谁
"""
return view

# CBV的精髓
def dispatch(self, request, *args, **kwargs):
# 获取当前请求的小写格式 然后比对当前请求方式是否合法
# get请求为例
# post请求
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
"""
反射:通过字符串来操作对象的属性或者方法
handler = getattr(自己写的类产生的对象, 'get',当找不到get属性或者方法的时候就会用第三个参数)
handler = 我们自己写的类里面的get方法
"""
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs) # 调用get方法

# 要求掌握到不看源码也能够描述出CBV的内部执行流程

模版语法

Django 模板语法个人不打算使用,故不记录。