一 视图函数的返回值
# urls.py
path('index/', views.index)
# views.py
def index(request):
pass
访问index路由,会报这个视图函数返回的不是一个HttpResponse对象,而是一个None。
言外之意是调用视图函数返回的必须是一个HttpResponse对象。
# 源码
class HttpResponse(HttpResponseBase):
pass
def render(
request, template_name, context=None, content_type=None, status=None, using=None
):
"""
Return an HttpResponse whose content is filled with the result of calling
django.template.loader.render_to_string() with the passed arguments.
"""
content = loader.render_to_string(template_name, context, request, using=using)
return HttpResponse(content, content_type, status)
def redirect(to, *args, permanent=False, **kwargs):
"""
Return an HttpResponseRedirect to the appropriate URL for the arguments
passed.
"""
redirect_class = (
HttpResponsePermanentRedirect if permanent else HttpResponseRedirect
)
return redirect_class(resolve_url(to, *args, **kwargs))
二 视图函数返回json格式数据
# urls.py
path('json/', views.json)
# views.py
def json(request):
user_dict = {"name": "jasper", "age": 18, "hobby": ["read", "run", "篮球"]}
import json
res = json.dumps(user_dict, ensure_ascii=False)
return HttpResponse(res)
结果:
# views.py
def json(request):
user_dict = {"name": "jasper", "age": 18, "hobby": ["read", "run", "篮球"]}
from django.http import JsonResponse
return JsonResponse(user_dict)
怎样解决中文乱码问题?
# 源码
class JsonResponse(HttpResponse):
def __init__(
self,
data,
encoder=DjangoJSONEncoder,
safe=True,
json_dumps_params=None,
**kwargs,
): # 初始化类对象
if json_dumps_params is None: # 当json_dumps_params参数不传时 默认转换成空字典
json_dumps_params = {}
kwargs.setdefault("content_type", "application/json")
# 调用json模块序列化 将json_dumps_params里的关键字参数转换成关键字实参传入dumps函数
data = json.dumps(data, cls=encoder, **json_dumps_params)
super().__init__(content=data, **kwargs)
dumps函数中不保证时ascii码的参数时ensure_ascii=True,只用将这个参数以字典的形式传入即可。
# views.py
def json(request):
user_dict = {"name": "jasper", "age": 18, "hobby": ["read", "run", "篮球"]}
from django.http import JsonResponse
return JsonResponse(user_dict, json_dumps_params={"ensure_ascii": False})
# views.py
def json(request):
user_dict = ["唱", "跳", "rap", "篮球"]
from django.http import JsonResponse
return JsonResponse(user_dict, json_dumps_params={"ensure_ascii": False})
报错信息说,不是一个字典类型对象不能被序列化,需要把safe参数设置成False。
# 源码
class JsonResponse(HttpResponse):
def __init__(
self,
data,
encoder=DjangoJSONEncoder,
safe=True,
json_dumps_params=None,
**kwargs,
):
# 这就是抛出异常的代码
# 当safe为True并且data的类型不是dict 则会抛出异常
if safe and not isinstance(data, dict):
raise TypeError(
"In order to allow non-dict objects to be serialized set the "
"safe parameter to False."
)
查看源码后发现,只需将safe参数改为False即可。
# views.py
def json(request):
user_dict = ["唱", "跳", "rap", "篮球"]
from django.http import JsonResponse
return JsonResponse(user_dict, json_dumps_params={"ensure_ascii": False}, safe=False)
三 form表单携带文件数据
# urls.py
path('form/', views.form)
# views.py
def form(request):
if request.method == 'GET':
return render(request, 'form.html')
print(request.POST)
return HttpResponse('ok')
<body>
<form method="post">
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" name="email" class="form-control" id="exampleInputEmail1" placeholder="Email">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" name="password" id="exampleInputPassword1" placeholder="Password">
</div>
<div class="form-group">
<label for="exampleInputFile">File input</label>
<input type="file" id="exampleInputFile" name="file">
<p class="help-block">Example block-level help text here.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox"> Check me out
</label>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</body>
request.POST拿到的是一个字符串并不是真正的文件对象,如果想拿到真正的数据,必须满足以下条件。
- from标签中请求方式必须是post
- from标签中必须有属性enctype=“multipart/from-data”
- 后端获取文件数据使用request.FILES
# views.py
def form(request):
if request.method == 'GET':
return render(request, 'form.html')
print(request.POST)
print(request.FILES)
return HttpResponse('ok')
<form method="post" enctype="multipart/form-data">
可以看到request.FILES拿到的可以看作是一个字典,使用方法和GET、POST一致。
将JQuery包导入Django中的static文件夹中:
# views.py
def form(request):
if request.method == 'GET':
return render(request, 'form.html')
file_obj = request.FILES.get('file')
path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'static', file_obj.name)
with open(path, 'a', encoding='utf8') as f:
for i in file_obj:
f.write(i.decode())
return HttpResponse('ok')
四 FBV和CBV
4.1 FBV
基于函数的视图,上面写的全是FBV的,这里就不在写了。
4.2 CBV
基于类的视图:
- 与特定的HTTP方法(GET、POST等)关联的代码组织能通过单独的方法替换条件分支来解决。
- 面向对象技术可用于将代码分解为可重用组件。
本质上来说,基于类的视图允许使用不同的类实例方法响应不同的HTTP请求,而不是在单个视图函数里使用有条件分支的代码。
因此,在CBV里处理HTTP中的GET和POST的代码应该像下边这么写。
# views.py
from django.shortcuts import HttpResponse, render
from django.views import View
# Create your views here.
class MyView(View):
def get(self, request):
return render(request, 'post.html')
def post(self, request):
return HttpResponse('post result')
<form action="" method="post">
<input class="btn btn-default" type="submit" value="Submit">
</form>
# urls.py
from django.urls import path
from app01.views import MyView
urlpatterns = [
path('test/', MyView.as_view()),
]
当触发get请求时:
点击submit触发post请求时:
4.3 CBV源码分析
因为Django的URL解析器期望发送请求和相关参数来调动函数而不是类,基于类的视图有一个as_view()类方法。
# View类中的as_view方法
class View:
...
@classonlymethod
def as_view(cls, **initkwargs):
...
return view
- 当一个URL被匹配时,会调用我们自己写的类MyView中的as_view方法,但是MyView中没有此方法,但是MyView继承了View类,其实就是执行了View类中的as_view,并返回一个view函数。
# View类中的view函数
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.setup(request, *args, **kwargs)
if not hasattr(self, "request"):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
- 执行view函数,先实例化一个MyView的对象self,再用这个对象调用setup方法(对象再找一个方法,先从对象本身找,再找产生对象的类,没有再找类的父类),这里的setup是View中的。
# View类中的setup方法
def setup(self, request, *args, **kwargs):
"""Initialize attributes shared by all view methods."""
if hasattr(self, "get") and not hasattr(self, "head"):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
- 运行setup初始MyView产生的对象,然后调用dispatch方法。
# View类中的dispatch方法
http_method_names = [
"get",
"post",
"put",
"patch",
"delete",
"head",
"options",
"trace",
]
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)
- 判断请求方式是不是在 http_method_names 中,如果在,就获取对象的该属性,因为MyView中定义了get方法和post方法,所以对象获取的就是自己本身的,执行并返回。如果不存在就会引发HttpResponseNotAllowed。