Django04 - 视图层
零:Django视图层
Django
视图层相关的知识都是在APP
下的views.py
中所应用的,视图层的主要功能就是如何处理由路由层解析过来的request
请求,主要包含以下几块内容
1.如何处理
request
请求对象2.如何返回一个
HTML
文档或其他内容给前端页面3.如何对接上数据库实时获取数据
一:请求对象
这里的request
是django封装的对象,它的类是WSGIRequest
,它里面包含了所有http请求的东西
print(request) # <WSGIRequest: GET '/upload/'>
print(type(request)) # <class 'django.core.handlers.wsgi.WSGIRequest'>
WSGIRequest
继承了http的HttpRequest
HttpRequest
中,内置了许多方法
请求对象的方法
request.method
- 返回请求的方法
print(request.method) # GET、POST等
request.GET
- QueryDict
对象,保存了用户提交过来的数据(不包含文件)
http://127.0.0.1:8000/index/?name=Darker&age=18 # 手动在地址栏填入的数据
print(request.GET) # <QueryDict: {'name': ['Darker'], 'age': ['18']}>
request.POST
- QueryDict
对象,保存了用户提交过来的数据(不包含文件)
http://127.0.0.1:8000/index/?name=Ben&gender=male # 手动在地址栏填入的数据
print(request.POST) # <QueryDict: {'name': ['Ben'], 'gender': ['male']}>
request.is_ajax()
- 判断是不是ajax请求
print(request.is_ajax()) # True / False
request.path
- 返回当前访问的路径
http://127.0.0.1:8000/index/?name=Darker&age=18
print(request.path) # /index/
request.get_full_path()
- 返回当前访问的完整路径
http://127.0.0.1:8000/index/?name=Darker&age=18
print(request.get_full_path()) # /index/?name=Darker&age=18
request.encoding
- 客户端向服务端传递时,使用的编码方法
print(request.encoding) # urlencoded / form-data / json
request.META
- *重要 | 包括了很多信息
print(request.META['SERVER_PORT']) # 服务的端口 8080
print(request.META['SERVER_PROTOCOL']) # 服务的HTTP协议 HTTP/1.1
print(request.META['REMOTE_ADDR']) # 访问的设备IP 127.0.0.1
print(request.META['CONTENT_TYPE']) # 客户端向服务端发送的编码方式 text/plain
print(request.META['HTTP_HOST']) # 主机IP 127.0.0.1:8080
print(request.META['HTTP_USER_AGENT']) # 访问设备的浏览器类型 Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36 SE 2.X MetaSr 1.0
print(request.META['HTTP_REFERER']) # 访问的前一个URL http://127.0.0.1:8080/index/
request.FILES
- 用户上传的文件
二:响应对象
HttpResponse - 接收一个字符串并返回
from django.shortcuts import HttpResponse
def return_HttpResponse(request):
return HttpResponse("Hello World")
render
render()
有多个参数,其中第一个参数为返回request
请求对象,其他参数则用于返回HTML
文档及其局部命名空间内变量用作模板渲染。
参数 | 内容 |
---|---|
request | 用于生成响应的请求对象 |
template_name | 要使用的模板的完整名称,可选的参数 |
context | 添加到模板上下文的一个字典 默认是一个空字典 如果字典中的某个值是可调用的,视图将在渲染模板之前调用它 局部命名空间变量(字典形式从换入),或locals()函数 |
from django.shortcuts import render
def return_render(request):
user_msg = {"name":"Darker","age":18,"gender":"male"}
return render(request,"login.html",{"user_msg":user_msg})
# 参数3:{"user_msg":user_msg} 只传递user_msg到HTML文档
# 参数3:locals() 将当前函数的命名空间(return_render)下的所有变量传递到HTML文档
redirect - 接收一个url
并返回,状态码为302
,即重定向
from django.shortcuts import redirect
def return_redirect(request):
return redirect("http://www.xuexianqi.top") # 跳转到www.xuexianqi.top | 这里的参数可以是一个路由,也可以是一个完整的URL
JsonResponse
Jsonresponse
是Django
中自带的一个基于json
模块的封装,可以直接返回json
类型的数据至模板层的前端页面。
1.只支持字典,如果想传递其他数据类型需要将
safe
设置为False
2.如果想让转换后的
json
格式字符串中存在中文,请设置参数json_dumps_params={"ensure_ascii":False}
,即关闭自动转码
from django.http import JsonResponse
def f1(request):
data = {"name":"Darker","age":18}
return JsonResponse(data,json_dumps_params={"ensure_ascii":False}) # 只支持字典,关闭自动转码
本质
redirect
、render
、JsonResponse
的本质,都是HttpResponse
# 这里以render为例,用HttpResponse来实现
def f1(request):
data = {"name":"Darker","age":18}
from django.template import Template,Context
res = Template("<h1>{{user}}<h1>") # 模板,将HTML代码渲染成模板
con = Context({"user":{"name":"Darker"}}) # 替换的内容封装成Context对象
ret = res.render(con) # 执行替换
return HttpResponse(ret)
三:CBV与FBV
FBV - Function Based View:基于函数的视图
FBV
是在视图层里通过函数的方式进行逻辑处理。
具体用法:
# urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url('^index/', views.index) # 这里的index后面不能加括号
]
# views.py
from django.shortcuts import HttpResponse
def index(request):
if request.method == 'POST':
return HttpResponse("这是POST请求")
else:
return HttpResponse("这是GET请求")
CBV - Class Based View:基于类的视图
CBV
是在视图层里通过类的方式进行逻辑处理。
具体用法:
# urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url('^index/', views.Index.as_view()) # 这里的as_view后面需要加上括号
]
# views.py
from django.shortcuts import HttpResponse
from django.views import View # 导入View
class Index(View): # 必须继承View
def get(self,request): # 当get请求来,执行该方法
return HttpResponse("这是GET请求")
def post(self,request): # 当post请求来,执行该方法
return HttpResponse("这是POST请求")
CBV源码分析
@classonlymethod # django封装的类方法,只能用于装饰class(类)
def as_view(cls, **initkwargs):
"""
Main entry point for a request-response process.
"""
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs): # 这是1个闭包函数,此时不会运行
self = cls(**initkwargs) # self=Index,这里的Index就是自己在views.py中定义的类
if hasattr(self, 'get') and not hasattr(self, 'head'): # 如果有Index类有get方法并且没有head方法
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs) # 将request对象传进去, 执行dispatch,并返回
view.view_class = cls # view.view_class = Index
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=()) # 这里的cls.dispatch就是Index.dispatch,但是并没有加括号去执行
return view # 返回view,接下来才执行。
1.请求来了,匹配成功之后,会在路由
中进行匹配
url('^index/', views.Index.as_view())
# 这里的 views.Index.as_view() 是一个函数的内存地址,或者说:一定是一个可调用对象
2.此时,会传一个request对象
进去
url('^index/', views.Index.as_view()(request))
3.本质就是内层函数的闭包函数view()
url('^index/', views.Index.view(request))
4.view()
中,执行了self.dispatch()
url('^index/', views.Index.dispatch(request))
def dispatch(self, request, *args, **kwargs):
# 说白了就是判断请求方式,是不是在这个 self.http_method_names 里头,如果在handler就是一个请求模式的方法,比如get或者post
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) # 执行并返回了
5.最后,if request.method.lower() in self.http_method_names
def dispatch(self, request, *args, **kwargs):
# 判断请求方式,是不是在这个 self.http_method_names 里,如果在handler就是一个请求模式的方法,比如get或者post
if request.method.lower() in self.http_method_names: # 这里 self.http_method_names 的 self 寻找顺序需要理清楚,这里的self,是Index
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs) # 执行并返回了
这里的self
就是Index
也就是说,我们可以在Index
中,自己定义一个http_method_names
列表,来限制允许的请求
class Index(View):
http_method_names = ['post', 'get'] # 表示只允许post和get请求
如果没有自定义的这个列表,那么就会去找View
的http_method_names
列表
class View(object):
"""
Intentionally simple parent class for all views. Only implements
dispatch-by-method and simple sanity checking.
"""
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
6.总结
as_view()
会执行View.dispatch()
这个方法,这个方法内部其实就会执行GET
或者POST
方法。
FBV装饰器
给FBV
添加装饰器,一般我们会单独将request
提取出来。
from django.shortcuts import HttpResponse
def wrapper(func):
def inner(request,*args,**kwargs):
return func(request,*args,**kwargs):
return inner
@wrapper
def other(request):
return HttpResponse("other...")
CBV装饰器
CBV
添加装饰器的方式有三种。针对不同的应用场景可以使用不同的装饰器添加方法,但是有一点是相同的。
都需要先导入下面这个模块:
from django.utils.decorators import method_decorator
方式1 - 为单独的某个功能添加装饰器
from django.shortcuts import HttpResponse
from django.views import View
from django.utils.decorators import method_decorator
def wrapp(func):
def inner(request,*args,**kwargs):
return func(request,*args,**kwargs):
return inner
class Other(View):
@method_decorator(wrapp)
def get(self, request):
return HttpResponse("get方法...")
@method_decorator(wrapp)
def post(self,request):
return HttpResponse("post方法...")
方式2 - 为CBV
类添加装饰器,可指定该装饰器作用与类下的那些方法(可添加多个)
from django.shortcuts import HttpResponse
from django.views import View
from django.utils.decorators import method_decorator
def wrapp(func):
def inner(request,*args,**kwargs):
return func(request,*args,**kwargs):
return inner
@method_decorator(wrapp,name="get")
@method_decorator(wrapp,name="post")
class Other(View):
def get(self,request):
return HttpResponse("get方法...")
def post(self,request):
return HttpResponse("post方法...")
方式3 - 为dispatch
方法添加装饰器,该装饰器会作用于所有的方法。(因为入口方法不是post
,就是get
)
from django.shortcuts import HttpResponse
from django.views import View
from django.utils.decorators import method_decorator
def wrapp(func):
def inner(request,*args,**kwargs):
return func(request,*args,**kwargs):
return inner
class Other(View):
@method_decorator(wrapp)
def dispatch(self, request, *args, **kwargs):
return super(Other,self).dispatch(request, *args, **kwargs)
def get(self,request):
return HttpResponse("get方法...")
def post(self,request):
return HttpResponse("post方法...")
CBV实现图片上传
要求:
1.在浏览器选择要上传的文件,点击按钮上传到指定文件夹
2.文件类型只能是图片类型
3.上传的图片需要在浏览器里展示出来
1.在项目路径下创建MEDIA文件夹
2.在MEDIA文件夹下创建upload_img文件夹
3.在settings.py中配置MEDIA的路径
4.在views.py中编写上传类
5.在templates文件夹中编写upload.html模板
6.在urls.py中配置路由
# views.py
class Upload(View):
def get(self, request):
img_list = []
from django.conf import settings
for media_path in [settings.MEDIA_ROOT, ]:
img_list.append(os.listdir(os.path.join(media_path, 'upload_img')))
return render(request, 'upload.html', {'img_list': img_list})
def post(self, request):
file = request.FILES.get('myfile')
if file:
if str(file.name).endswith('jpg' or 'jpeg' or 'bmp' or 'gif' or 'png'):
from django001 import settings
path = os.path.join(settings.MEDIA_ROOT, "upload_img", file.name)
with open(path, 'wb') as f:
for line in file.chunks():
f.write(line)
return HttpResponse('图片上传成功')
else:
return HttpResponse('文件类型错误')
else:
return HttpResponse('请选择文件')
# upload.html
<div class="row">
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-heading">图片上传</div>
<div class="panel-body">
<div class="col-md-4">
<form action="" method="post" enctype="multipart/form-data">
<br><br>
<input type="file" name="myfile" accept="image/*"><br>
<button type="submit" class="btn btn-default btn-block">上传图片</button>
<br><br>
</form>
</div>
</div>
</div>
</div>
<div class="col-md-9">
<div class="panel panel-default">
<div class="panel-heading">图片列表</div>
<div class="panel-body x-img">
{% for img in img_list %}
{% for img1 in img %}
<img src="/media/upload_img/{{ img1 }}">
{% endfor %}
{% empty %}
<h2 class="text-center">暂无照片</h2>
{% endfor %}
</div>
</div>
</div>
</div>
# settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, "media").replace("\\", "/")
# urls.py
urlpatterns = [
url(r'^upload/', views.Upload.as_view()),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)