一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应。
响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片。
无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你当前项目目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了将代码放在某处,大家约定成俗将视图放置在项目(project)或应用程序(app)目录中的名为views.py的文件中。
一个简单的视图函数
from django.http import HttpResponse
#从shortcuts 里面导入HttpResponse 也可以 http 和shortcuts 都是继承于HttpResponseBase
from django.shortcuts import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "It is now %s." % now
return HttpResponse(html)
让我们来逐行解释下上面的代码:
首先,我们从 django.http模块导入了HttpResponse类,以及Python的datetime库。
接着,我们定义了current_datetime函数。它就是视图函数。每个视图函数都使用HttpRequest对象作为第一个参数,并且通常称之为request。
注意,视图函数的名称并不重要;不需要用一个统一的命名方式来命名,以便让Django识别它。我们将其命名为current_datetime,是因为这个名称能够比较准确地反映出它实现的功能。
这个视图会返回一个HttpResponse对象,其中包含生成的响应。每个视图函数都负责返回一个HttpResponse对象。
Django使用请求和响应对象来通过系统传递状态。
当浏览器向服务端请求一个页面时,Django创建一个HttpRequest对象,该对象包含关于请求的元数据。然后,Django加载相应的视图,将这个HttpRequest对象作为第一个参数传递给视图函数。
每个视图负责返回一个HttpResponse对象。
视图层,熟练掌握两个对象即可:请求对象(request)和响应对象(HttpResponse)
官方文档
1.request请求相关的属性和方法:
示例:
def index(request): #http请求相关信息 封装的HttpRequest对象
#比较常用的
if request.method == 'GET':
print(request.GET) # 包含所有HTTP GET参数的类字典对象
print(request.META) #请求头相关内容
print(request.path) #一个字符串,表示请求的路径组件(不含域名)。例如:"/music/bands/the_beatles//app02/index/"
print(request.path_info) #返回用户访问url,不包括域名 /app02/index/
print(request.get_full_path()) #/app02/index/?dazhuang=123 除了域名和端口 其他的都要
print(request.body) # 请求体,byte类型 request.POST的数据就是从body里面提取到的 原始数据
return render(request, 'index.html')
else:
print(request.body) # 请求体,byte类型 request.POST的数据就是从body里面提取到的 原始数b'username=avdsv'
print(request.method) #请求中使用的HTTP方法的字符串表示,全大写表示。
print(request.POST) #包含所有HTTP POST参数的类字典对象
return HttpResponse('ok')
属性:
django将请求报文中的请求行、头部信息、内容主体封装成 HttpRequest 类中的属性。
除了特殊说明的之外,其他均为只读的。
0.HttpRequest.scheme(后面再学)
表示请求方案的字符串(通常为http或https)
1.HttpRequest.body(后面再学)
一个字符串,代表请求报文的主体。在处理非 HTTP 形式的报文时非常有用,例如:二进制图片、XML,Json等。
但是,如果要处理表单数据的时候,推荐还是使用 HttpRequest.POST ,因为POST中的数据从 request.body里获取数据 然后再通过json.loads(request.body)组成一个QueryDict对象。
另外,我们还可以用 python 的类文件方法去操作它,详情参考 HttpRequest.read() 。
2.HttpRequest.path
一个字符串,表示请求的路径组件(不含域名)。
例如:"/music/bands/the_beatles/"
3.HttpRequest.method
一个字符串,表示请求使用的HTTP 方法。必须使用大写。
例如:“GET”、“POST”
4.HttpRequest.encoding
一个字符串,表示提交的数据的编码方式(如果为 None 则表示使用 DEFAULT_CHARSET 的设置,默认为 ‘utf-8’)。
这个属性是可写的,你可以修改它来修改访问表单数据使用的编码。
接下来对属性的任何访问(例如从 GET 或 POST 中读取数据)将使用新的 encoding 值。
如果你知道表单数据的编码不是 DEFAULT_CHARSET ,则使用它。
5.HttpRequest.GET
一个类似于字典的对象,包含 HTTP GET 的所有参数。详情请参考 QueryDict 对象。
6.HttpRequest.POST
一个类似于字典的对象,如果请求中包含表单数据,则将这些数据封装成 QueryDict 对象。
POST 请求可以带有空的 POST 字典 —— 如果通过 HTTP POST 方法发送一个表单,但是表单中没有任何的数据,QueryDict 对象依然会被创建。
因此,不应该使用 if request.POST 来检查使用的是否是POST 方法;应该使用 if request.method == “POST”
另外:如果使用 POST 上传文件的话,文件信息将包含在 FILES 属性中。
7.HttpRequest.COOKIES
一个标准的Python 字典,包含所有的cookie。键和值都为字符串。
8.HttpRequest.FILES
一个类似于字典的对象,包含所有的上传文件信息。
FILES 中的每个键为 中的name,值则为对应的数据。
注意,FILES 只有在请求的方法为POST 且提交的 带有enctype=“multipart/form-data” 的情况下才会
包含数据。否则,FILES 将为一个空的类似于字典的对象。
9.HttpRequest.META
一个标准的Python 字典,包含所有的HTTP 首部(请求头信息)。具体的头部信息取决于客户端和服务器,下面是一些示例:
CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
CONTENT_TYPE —— 请求的正文的MIME 类型。
HTTP_ACCEPT —— 响应可接收的Content-Type。
HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
HTTP_HOST —— 客服端发送的HTTP Host 头部。
HTTP_REFERER —— Referring 页面。
HTTP_USER_AGENT —— 客户端的user-agent 字符串。
QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。
REMOTE_ADDR —— 客户端的IP 地址。
REMOTE_HOST —— 客户端的主机名。
REMOTE_USER —— 服务器认证后的用户。
REQUEST_METHOD —— 一个字符串,例如"GET" 或"POST"。
SERVER_NAME —— 服务器的主机名。
SERVER_PORT —— 服务器的端口(是一个字符串)。
从上面可以看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 之外,请求中的任何 HTTP 首部转换为 META 的键时,
都会将所有字母大写并将连接符替换为下划线最后加上 HTTP_ 前缀。
所以,一个叫做 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。
10.HttpRequest.user
一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户。
如果用户当前没有登录,user 将设置为 django.contrib.auth.models.AnonymousUser 的一个实例。你可以通过 is_authenticated() 区分它们。
例如:
if request.user.is_authenticated():
# Do something for logged-in users.
else:
# Do something for anonymous users.
user 只有当Django 启用 AuthenticationMiddleware 中间件时才可用。
------------------------------------------------------------------------------------
匿名用户
class models.AnonymousUser
django.contrib.auth.models.AnonymousUser 类实现了django.contrib.auth.models.User 接口,但具有下面几个不同点:
id 永远为None。
username 永远为空字符串。
get_username() 永远返回空字符串。
is_staff 和 is_superuser 永远为False。
is_active 永远为 False。
groups 和 user_permissions 永远为空。
is_anonymous() 返回True 而不是False。
is_authenticated() 返回False 而不是True。
set_password()、check_password()、save() 和delete() 引发 NotImplementedError。
New in Django 1.8:
新增 AnonymousUser.get_username() 以更好地模拟 django.contrib.auth.models.User。
11.HttpRequest.session
一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。
完整的细节参见会话的文档。
2.response响应相关的方法:
与由Django自动创建的HttpRequest对象相比,HttpResponse对象是我们的职责范围了。我们写的每个视图都需要实例化,填充和返回一个HttpResponse。
def response(request):
HttpResponse ----回复字符串的时候来用
HttpResponse.content:响应内容
HttpResponse.charset:响应内容的编码
HttpResponse.status_code:响应的状态码
render -------回复一个html页面的时候使用
参数:
request: 用于生成响应的请求对象。
template_name:要使用的模板的完整名称,可选的参数
context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。
content_type:生成的文档要使用的MIME类型。默认为 DEFAULT_CONTENT_TYPE 设置的值。默认为'text/html'
status:响应的状态码。默认为200。
useing: 用于加载模板的模板引擎的名称。
redirect ----重定向给浏览器了一个30x的状态码
参数可以是:
1. 一个模型:将调用模型的get_absolute_url() 函数
2.一个视图,可以带有参数:将使用urlresolvers.reverse 来反向解析名称
3.一个绝对的或相对的URL,将原封不动的作为重定向的位置。
默认返回一个临时的重定向;传递permanent=True 可以返回一个永久的重定向。
示例:
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'test' and password == 'test123456':
# return render(request, 'home.html')
return redirect('app02:home')
else:
return HttpResponse('输入错误!!!')
#首页
def home(request):
return render(request,'home.html')
实际上response中的redirect和render最后走的还是HttpResponse,来看下源码:
redirect源码分析:
if kwargs.pop('permanent', False):
redirect_class = HttpResponsePermanentRedirect #这里是调用我们的HTTPResponse相关类
else:
redirect_class = HttpResponseRedirect
return redirect_class(resolve_url(to, *args, **kwargs)) #将我们的app02:home传递进去
我们再看HttpResponsePermanentRedirect 和HttpResponseRedirect有没有父类
这里可以看到这两个类一个是临时重定向(302)和永久重定向(301),并且同时都是继承自HttpResponseRedirectBase这个类,我们看HttpResponseRedirectBase这个类继承自谁:
这里就可以看到了,最后还是继承自我们的HttpResponse这个类。
render源码分析
最后调用的还是我们HttpResponse。
3. 301和302
1)301和302的区别。
301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取
(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。
他们的不同在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;
302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。 SEO302好于301
2)重定向原因:
(1)网站调整(如改变网页目录结构);
(2)网页被移到一个新地址;
(3)网页扩展名改变(如应用需要把.php改成.Html或.shtml)。
这种情况下,如果不做重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户得到一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的
网站,也需要通过重定向让访问这些域名的用户自动跳转到主站点等。
临时重定向(响应状态码:302)和永久重定向(响应状态码:301)对普通用户来说是没什么区别的,它主要面向的是搜索引擎的机器人。
A页面临时重定向到B页面,那搜索引擎收录的就是A页面。
A页面永久重定向到B页面,那搜索引擎收录的就是B页面。
FBV(function base views) 就是在视图里使用函数处理请求。
之前都是FBV模式写的代码,所以就不写例子了。
CBV(class base views) 就是在视图里使用类处理请求。
Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。所以Django在后来加入了Class-Based-View。可以让我们用类写View。这样做的优点主要下面两种:
1.提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
2.可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
示例:
views.py中写法:
# CBV
from django.views import View
class LoginView(View):
def get(self,request): #方法名必须要是get
return render(request,'login2.html')
def post(self,request):
username = request.POST.get('username')
password = request.POST.get('password')
print(username)
print(password)
return HttpResponse('ok')
urls.py中写法:
urlpatterns = [
# CBV
url(r'^login2/', views.LoginView.as_view(),name='login2'),
]
考虑一下,为什么get请求和post请求就能找到get方法和post方法,请求过程时什么样的,这里给大家分析一下:
上面我们urls.py文件中配置的路径是:url(r’^login2/’, views.LoginView.as_view(),name=‘login2’),这里有个as_view()方法,意思是一个请求过来是要先执行这个as_view()方法,我们之前写的那种是先请求然后内部再调用,而as_view()在调用之前已经是执行了,所以一个请求过来肯定会执行as_view()里面的逻辑,我们写的类里面并没有写as_view()这个方法,肯定父类里面有这个方法,现在我们来看一as_view()这个方法:
@classonlymethod
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):
self = cls(**initkwargs) #实例化一个LoginView视图类的对象
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
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=())
return view
源码里面可以看到是有as_view()这个方法的,然后给我们的视图类LoginView实例化一个对象self,最后又调用了一个dispatch这个方法,我们来看一下dispatch这个方法:
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)
我们看一下dispatch这个方法干了哪些事情,先判断你的请求方法在不在http_method_names这个里面,看一下http_method_names这里里面都是什么:
里面定义的都是我们常用的请求方法,如果在这个里面就执行下面的这行代码,handler = getattr(self, request.method.lower(), self.http_method_not_allowed) ,这行代码什么意思,利用反射去获取我们这个对象里面的方法,如果有就返回去供as_view这个方法使用,如果没有就执行self.http_method_not_allowed这里面的内容,我们看一下这个里面逻辑:
关键点:
可以看到是一个日志,warning级别的日志,提示你不允许的方法,说明是请求错误,我们这里将我们视图类中的方法名故意写错试试看:
这里是将get和post都改成get1和post1:
浏览器中我们可以看到是请求不成功的,并且给我们返回一个405的状态码,和我们self.http_method_not_allowed中提示的一样。
重写dispatch方法
如果我们类里面定义了dispatch方法,首先会执行我们类里面的dispatch方法,从上面的分析我们可以看到dispatch会在分发请求之前先执行dispatch这个方法,是不是我们可以在视图类中重写这个方法,然后在他前后拓展一些内容,一个请求过来肯定先走dispatch这个方法,在分发之前我做一些事情是不是可以,大家想想是不是这样:
修改后:
from django.views import View
class LoginView(View):
def dispatch(self, request, *args, **kwargs):
print('请求来了') #拓展内容
ret = super().dispatch(request, *args, **kwargs)
print('请求走了') #拓展内容
return ret
def get(self,request): #方法名必须要是get
print('get方法执行了')
return render(request,'login2.html')
def post(self,request):
username = request.POST.get('username')
password = request.POST.get('password')
print(username)
print(password)
return HttpResponse('ok')
为什么重写dispatch要返回一个值,dispatch最终还是要返回获取到的方法进行执行:
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)
‘’‘
handler的值是不是我们获取到的方法名,然后加括号是不是执行,这句话就类似于
return render(request,'login2.html')最后又被as_view调用,
因为as_view里面实例化了视图类LoginView,视图类要用到dispatch这个方法进行请求分发,
所以最后会被as_view所调用,而每个视图函数都要返回一个HTTPResponse对象,
所以在视图里面重写dispatch方法的时候必须要返回一个值
’‘’
上面代码执行结果:
如果想给单独某个视图函数前后加些内容,可以使用装饰器:
FBV加装饰器:
def wrapper(f):
def inner(*args,**kwargs):
print('请求之前')
ret = f(*args,**kwargs)
print('请求之后')
return ret
return inner
@wrapper
def app02(request):
print(request.path_info)
return render(request,'app02.html')
结果:
请求之前
/app02/
请求之后
CBV加装饰器:
from django.views import View
from django.utils.decorators import method_decorator #先导入装饰器方法 方法装饰器
@method_decorator(wrapper,name='get') #方式三:视图类上面加,name指定给那个请求加装饰器
class LoginView(View):
# @method_decorator(wrapper) #方式二:给所有请求加
def dispatch(self, request, *args, **kwargs):
# print('请求来了')
ret = super().dispatch(request, *args, **kwargs)
# print('请求走了')
return ret
# @method_decorator(wrapper) #方式一:单独给get请求加
def get(self,request): #方法名必须要是get
print('get方法执行了')
return render(request,'login2.html')
def post(self,request):
print('post方法执行了')
username = request.POST.get('username')
password = request.POST.get('password')
print(username)
print(password)
return HttpResponse('ok')