Django笔记总结(二) -- 路由规则、用户请求、视图函数
一、用户请求生命周期
用户从浏览器点击我们的网站发出请求开始,这个请求就有一个生命周期,请求经历的生命周期如下所示:
用户请求 —> 中间件 —> 路由匹配 —> 视图函数 —> 数据操作 —> 模板渲染 —> 中间件 —> 响应结果
有关中间件的内容将放在后续的博客中介绍
二、用户请求
用户发送的请求常见的有两种:GET、POST。
1、GET请求
当用户在浏览器的地址栏中输入一个网址时,这时用户就发送了一个GET请求,GET请求是将用户数据封装在HTTP请求头中传给后台的,数据会暴露在浏览器的地址栏中,因此一定意义上GET请求是不安全的请求,如下所示就是一个GET请求:
https://i.cnblogs.com/EditArticles.aspx?opt=1
在GET请求中,使用“?”来分隔URL和参数,而多个参数之间以“&”符号分隔,上述请求中的opt就是一个参数,该参数的值是1
在html代码中,form表单默认采用GET的方式提交数据,可以通过method="post"来设置为使用POST方式提交。
2、POST
POST请求相对GET请求来要更安全一些,因为POST请求不会封装在http请求头中,而是封装在HTTP请求的数据体中发送给后端服务器,所以在发送POST请求时,在地址栏上是看不到发送的数据的内容的,所以这种方式也相对安全
三、路由匹配
从用户请求的生命周期可以看到,当用户发送请求到达Django服务器时,服务器会根据用户请求的URL来判断调用哪个函数来处理用户请求,这个判断的规则就是路由匹配规则,这个判断的过程就是路由匹配。
在工程同名目录下我们可以看到有一个urls.py的文件,这个文件就是全局的路由匹配规则的配置文件。
1、一级URL匹配
我们在工程同名目录下的urls.py文件中定义一条路由匹配规则如下:
from django.conf.urls import url from django.contrib import admin #导入HttpResponse用来给用户返回一个字符串 from django.shortcuts import HttpResponse #定义一个函数,当路由匹配成功后会调用该函数来处理用户请求 def test(): return HttpResponse("OK") urlpatterns = [ url(r'^admin/', admin.site.urls), #添加一条路由匹配规则 url(r'^test/',test), ]
从上述代码中我们可以看到,我们在这个文件中定义了一个函数test(),这个函数会给用户返回一个字符串“OK”,同时,我们在urlpatterns这个配置项中添加了一条路由匹配规则 "url(r'^test/',test)," 这条规则的意思是当用户访问/test/这个URL时,就调用test函数来处理用户请求,我们访问以下地址:
我们可以看到,服务器成功返回了字符串"OK"。
我们刚才直接在全局路由规则文件中写入了一条路由规则,这条规则直接找到了用来处理用户请求的函数,这样的路由规则就是一个一级路由匹配。
2、二级路由匹配
我们可以看到,一个网站由很多功能块组成,其中的URL也是五花八门,数量也是很多,如果这些URL对应的路由匹配规则全部写在一个文件中,那无疑会导致这个文件很繁杂,不利于维护,而且如果一个app的URL发生了改变,所有的路由规则都需要重新做一次修改,很显然这是十分不利的,所以,我们可以使用二级路由匹配来吧不同app的路由条目分开管理。
比如我们新建一个app,名字叫admin:
我们可以看到,新建的app目录下并没有urls.py这个文件,所以我们需要自己创建这个文件:
#!/usr/bin/env python3 # -*- coding:utf-8 -*- from django.conf.urls import url #导入app中的views模块 from admin import views urlpatterns = [ #当URL中包含index时,调用views文件中的index函数处理 url(r'^index/',views.index), ]
从上述代码中我们可以看到,当我们访问index/这个URL时,将会调用views中的index函数来处理,但是用户请求如何能找到我们app的路由规则呢?这时就需要在全局的路由匹配文件中定义包含进app的路由规则文件了,所以,在工程同名目录的urls.py文件中添加如下内容:
#导入include模块 from django.conf.urls import url,include urlpatterns = [ #当用户访问/admin/这个URL时,使用admin中的urls文件来进行路由匹配 url(r'^admin/',include('admin.urls')) ]
上述配置意味着,当用户访问的URL中以admin/开头的请求都会使用admin.urls这个文件来做路由匹配,也就是说,用户在访问/admin/index/这个URL时,会调用index函数处理:
在上述路由匹配过程中,首先在全局路由配置中匹配到了admin,然后又在app的路由匹配中匹配到了index,共进行了两次匹配,所以这种路由匹配称之为二级路由匹配,其中需要使用include将第二级的urls文件包含金全局的路由配置文件中。
3、单一路由匹配
从上述例子中可以看到,我们进行路由匹配时,一条路由规则只能匹配到一个URL,这样子的匹配就是单一路由匹配,很明显,这种方式具有较大的局限性。
4、基于正则的路由匹配
我们可以在路由匹配时加入正则表达式,正则表达式匹配URL成功后就认为路由匹配成立:
正则表达式 | 匹配结果 |
. | 匹配单个字符 |
\d | 匹配数字 |
\w | 匹配除数字,字母组成的字符串 |
[a-z] | 匹配小写字母 |
[A-Z] | 匹配大写字母 |
[A-Za-z] | 匹配所有字母 |
+ | 匹配该字符前的字符一次或多次 |
? | 匹配该字符前的字符零次或一次 |
* | 匹配该字符前的字符零次或多次 |
[^/] | 匹配非“/”的字符串 |
{1,4} | 匹配1个或2个或3个或4个前一个字符 |
例如,我们在全局url配置中改为如下配置:
url(r'^admin\w+/',include('admin.urls')),
这样,我们在访问URL中,访问类似如下的URL都是可以访问到的:
http://127.0.0.1:8001/adminadqerq423rt32f/index/
http://127.0.0.1:8001/adminaeadfrq9ad82/index/
5、带参数的路由匹配
如果我们在写路由匹配规则的时候,加入分组,那么分组的数据会作为参数传递给后端的函数,例如给上述:
url(r'^index/(\d{4})/(\d{1,2})/',views.index),
这样一条路由匹配规则,当用户访问/admin/index/2017/02/这个URL时,2017和02就会作为参数传递给后端的函数,此时后端函数需要接收一下这两个参数,否则就会报错,例如views中index函数如下:
from django.shortcuts import render,HttpResponse # Create your views here. def index(request,*args,**kwargs): return HttpResponse("This is index page! %s-%s" %(args[0],args[1]))
当我们访问上述URL时,我们得到的结果就如下结果:
This is index page! 2017-02
除了上述的传递参数的方法外,我们还可以通过给参数命名的方式来给后端函数传参数,例如上述路由匹配规则改为如下形式:
url(r'^index/(?P<year>\d{4})/(?P<month>\d{1,2})/',views.index),
其中(?P<name>pattern)这部分中的name就是该参数的参数名,views中的index函数应该改成如下:
from django.shortcuts import render,HttpResponse # Create your views here. def index(request,**kwargs): return HttpResponse("This is index page! %s-%s" %(kwargs['year'],kwargs['month']))
6、命名路由匹配
当我们需要在HTML中提交表单时,表单内容需要提交到一个URL中去,无疑,这个URL在找对应的函数时也需要通过路由匹配,那么如果我们此时把这个路由匹配给改变了,那么我们在html中就需要手动改变表单提交的URL,如果页面中这样的URL有很多……,显然,这种方式是不方便的,所以,Django中提供了一个命名路由匹配的功能,就是给我们的路由条目起个名字,前端页面上直接使用这个名字来动态的生成URL,这样一来,上面提到的问题就迎刃而解了。具体方法如下:
#!/usr/bin/env python3 # -*- coding:utf-8 -*- from django.conf.urls import url from admin import views urlpatterns = [ url(r'^index/',views.index,name="test1"), # url(r'^index/(\d{4})/(\d{1,2})/',views.index), ]
在urls文件中,我们给路由条目添加一个name属性,这就是给该路由条目命名的名字,接下来,我们在html文件中可以通过模板语言来使用,关于模板语言的详细介绍将放在后面的博客中。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="{% url 'test1' %}" method="post"> <input type="submit" value="提交"> </form> </body> </html>
对应的我们的views函数也需要做相应的更改。
from django.shortcuts import render,HttpResponse # Create your views here. def index(request): if request.method == "POST": return HttpResponse("This is index page!") elif request.method == "GET": return render(request,"test.html")
我们访问一下/admin/index/这个URL可以看到得到了刚才html文件中的页面,查看该页面中form表单的URL可以看到,其URL就是我们命名的那个URL。
当我们的路由匹配条目需要传递参数时,我们依然可以使用路由命名,只需要在html中跟上具体的参数即可,如果路由条目中的参数有名称,那么html中需要指定参数的名称:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="{% url 'test1' 2345 23 %}" method="post"> <input type="submit" value="提交"> </form> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="{% url 'test1' year=2017 month=03 %}" method="post"> <input type="submit" value="提交"> </form> </body> </html>
7、命名空间
四、视图函数
1、CBV与FBV
在views函数中,不仅可以使用函数的形式来定义,还可以使用面向对象的方式,面向对象的方式被称为CBV(Class Base Views),采用函数定义的被称为FBV(Function Base Views),使用函数的形式定义的此处不再赘述。
CBV定义的方式中,上述views中的函数可以如下定义:
from django.shortcuts import render,HttpResponse from django.views import View # Create your views here. class index(View): def get(self,request): return render(request, "test.html") def post(self,request): return HttpResponse("This is index page!")
在路由匹配规则中,引用CBV的方法如下:
#!/usr/bin/env python3 # -*- coding:utf-8 -*- from django.conf.urls import url from admin import views urlpatterns = [ url(r'^index/',views.index.as_view(),name="test1"), ]
2、HttpResponse、redirect、render
HttpResponse:返回一个字符串,用法:return HttpResponse("返回的字符串")
redirect:重定向到一个新的URL,用法:return redirect('重定向的URL')
render:将html模板渲染后返回给前端,用法:return render(request,'模板html文件',{'参数名':参数值,......})