转载请注明来源地址和原作者(CFishHome)

前沿

上一节的学前准备工作和第一个小牛试刀的Django项目学习,让我们对Django开发越来越感兴趣了。正所谓趁热打铁,让我们继续来学习网站开发必备的视图函数和URL映射等知识,跟着步伐一起学习,我相信你会收获很多。噔~噔,新闻播报时间:9月12-9月16有两个超强台风在广东湛江登陆,沿途影响众多城市,包括深圳!!所以很遗憾,前几天打算和舍友去深圳游玩的计划泡汤了,也只能推迟几天前往深圳度中秋、看月亮了~

DEBUG模式

无论是使用命令行还是Pycharm创建Django项目工程,settings.py文件的DEBUG都默认设置为True,所以默认是开启了DEBUG模式。如下图所示: 开启DEBUG模式有以下好处: 1.我们可以发现,在Django开发时,频繁修改文件代码,然后要关闭项目,再重启项目才能测试我们修改的代码。但是如果开启了DEBUG模式,那么以后我们修改了Django项目的代码,然后按下了ctrl+s,那么Django就会自动的给我们重启项目,不需要手动关闭再重启。 2.如果开启了DEBUG模式,那么以后Django项目中的代码出现bug了,那么在浏览器和控制台会打印出错信息。 3.在实际将项目发布到网上给其他用户使用时,禁止开启DEBUG模式,因为前面说了入宫出现bug会在浏览器和控制台打印出错信息,这无疑把代码泄露在网上,会有很大的安全隐患。 4.如果DEBUG设置为False,那么必须设置ALLOWED_HOSTS。前一篇说过,如果将host设置为0.0.0.0,那么只能通过ALLOWED_HOSTS设置的IP地址进行访问该项目,也可以设置多个IP地址。ALLOWED_HOSTS字段如下图位置:

URL分发器

我们在学习前先回顾下我们上一篇做了什么。上一篇我们首先在Pycharm创建了一个名为first_project的Django项目,然后在CMD使用命令创建了一个名为my_app的app应用。注意,上篇因测试需要修改了IP地址和端口号,所以这里我将项目的IP地址和端口号修改回127.0.0.1:8000,说明访问这个IP就可以访问我们的项目了。整体框架如下图所示:

视图

视图其实就是视图函数,它一般都写在 app 的 views.py 文件中,也就是我们my_app包里的views.py文件。并且视图函数的第一个参数永远都是 request (一个HttpRequest)对象。这个对象存储了请求过来的所有信息,包括携带的参数以及一些头部信息等。在视图中,一般是完成逻辑相关的操作。比如这个请求是添加一篇博客,那么在视图函数可以通过request来接收到这些数据,然后存储到数据库中,最后再把函数执行的结果返回给浏览器。视图函数的返回结果必须是 HttpResponseBase 对象或者子类的对象。 我们打开my_app的views.py文件,添加以下代码:

from django.http import HttpResponse

# book_list是我们定义的一个视图函数,第一个参数必须是request对象,并且该函数返回必须是 HttpResponseBase  对象或者子类的对象。
def book_list(request): 
			return HttpResponse("书籍列表!")

上面视图函数返回的 HttpResponse("书籍列表!")只是单纯的在网页上输出"书籍列表!"文本内容。

URL映射

视图函数写完后,我们怎么实现让用户在浏览器中输入一个URL就可以访问到我们刚才写的视图函数,即用户在浏览器一输入某URL,网页就显示"书籍列表!"文本内容。

URL映射原理:用户在浏览器输入了某个 URL ,请求到我们的网站的时候, django 会从项目的 urls.py 文件中寻找对应的视图。为什么会是在urls.py文件中寻找映射呢?这是因为在"settings.py"文件中配置了"ROT_URLCONF"为"urls.py"。在 urls.py 文件中有一个 urlpatterns 变量,以后 django 就会从这个变量中读取所有的匹配规则。匹配规则需要使用 django.urls.path 函数进行包裹,这个函数会根据传入的参数返回 URLPattern 或者是 URLResolver 的对象。

修改first_project项目的 urls.py 文件代码,示例代码如下:

from django.contrib import admin
from django.urls import path
from my_app import views  # 导入my_app包的views.py模块

urlpatterns = [
    path('admin/', admin.site.urls),
    path('book/', views.book_list)
]

运行first_project项目,在浏览器输入127.0.0.1:8000/book,访问成功!如下图所示(图片左下角显示了"书籍列表!"文本内容): 到了这里朋友们可能会好奇如果输入127.0.0.1:8000会怎么样?满足你的好奇心,结果如下: 我的天!我按照上一篇的流程可以访问的啊,怎么现在不彳亍了。其实当新添加了第一个URL映射之后需要注意的是主网页127.0.0.1:8000的网页404丢失了,这是因为如果刚开始创建的项目,若还未添加过URL映射,那么默认Django的底层提供一个就是那个火箭??页面来映射,但若添加了,那么就无法继续映射那个火箭??页面了,因为看urlpatterns变量其实都没有映射空URL,即127.0.0.1:8000为空URL。

为URL传递参数

为URL传递参数有三种方式:

1.URL中添加参数

2.查询字符串传递参数

3.指定默认的参数

URL中添加参数

有时候, url 中包含了一些参数需要动态调整。比如简书某篇文章的详情页的url,是 https://www.jianshu.com/p/a5aab9c4978e 后面的 a5aab9c4978e 就是这篇文章的 id ,那么简书的文章详情页面的url就可以写成 https://www.jianshu.com/p/<id> ,其中id就是文章的id。那么如何在 django 中实现这种需求呢。这时候我们可以在 path 函数中,使用尖括号的形式来定义一个参数,也可以多个参数。比如我现在想要获取一本书籍的详细信息,那么应该在 url 中指定这个参数。first_project项目urls.py文件的示例代码如下:

from django.contrib import admin
from django.urls import path
from book import views

urlpatterns = [
		path('admin/', admin.site.urls),
		path('book/',views.book_list),
		path('book/<book_id>/',views.book_detail)  # <book_id>就是参数
]

采用在URL中使用变量的方式,在path函数的第一个参数中,使用“<参数名>”的方式可以传递参数,然后在视图函数中也要写一个函数来进行映射,视图函数中的参数必须和URL中的参数名称保持一致,不然就找不到这个参数(视图函数第一个参数是request,那么第二个参数就是book_id)。 而my_app包的views.py 中的代码如下:

# 该视图函数与'book/<book_id>/'进行URL映射,并且视图函数第二个参数必须同名
def book_detail(request,book_id):
		text = "您输入的书籍的id是:%s" % book_id
		return HttpResponse(text)

运行结果如下: 对于多个参数也同样类似,这里不再测试。只不过在URL中添加多个参数,然后视图函数添加多个同名参数来接收。

查询字符串传递参数

另一种方式是通过查询字符串的方式传递一个参数过去。在设置URL时,不需要单独的匹配查询字符串的部分,只需要在视图函数中使用request.GET.get('参数名称')的方式来获取。因为查询字符串使用的是“GET”请求,所以我们通过“request.GET”来获取参数。 first_project项目的urls.py文件的示例代码如下:

urlpatterns = [
		path('admin/', admin.site.urls),
		path('book/',views.book_list),
		path('book/<int:book_id>/',views.book_detail),  # <book_id>就是参数,注意book_id前面还添加了个int转换器,这里只要我们知道这是限制输入的id只能是整型即可,后面会详细讲解转换器的作用。
		path('book/pub/',views.book_pub)
]

在 my_app包中的views.py 后面添加如下代码:

def book_pub(request):
		pub_id = request.GET.get("id")
		text = "您输入的出版社id是:%s" % pub_id
		return HttpResponse(text)

以后在浏览器输入127.0.0.1:8000/book/pub/?id=1 即可将参数传递过去。 运行结果如下: 我们做个小测试,如果将转换器Int去掉的话,我们还能成功访问到嘛?将urls.py文件代码修改如下:

urlpatterns = [
		path('admin/', admin.site.urls),
		path('book/',views.book_list),
		path('book/<book_id>/',views.book_detail),  
		path('book/pub/',views.book_pub)
]

运行结果如下: 通过上图可以发现,跟我们预期的不太一样,我们在浏览器输入的URL(http://127.0.0.1:8000/book/pub/?id=200) 被book_detail视图函数捕获了,这说明pub后面的所有字符串都被认为是传递到book_detail视图函数。

指定默认的参数

使用 path 后,在path的第一个参数可以包含参数,而有时候想指定默认的参数,这时候可以通过以下方式来完成,以下代码仅供理解默认参数的使用,在我们的实际项目中没有此代码。

from django.urls import path
from . import views
urlpatterns = [
path('blog/', views.page),
path('blog/page<int:num>/', views.page),
]
# View (in blog/views.py)
def page(request, num=1):
# Output the appropriate page of blog entries, according to num.

上面'blog/pageint:num/',参数前面多了个page,代表输入URL时需要添加page,例如:’127.0.0.1:8000/blog/page100‘。所欲,如果你只写’127.0.0.1:8000/blog/page',那么将使用默认参数,num值为1。

默认参数的使用规则如下: 1.当用户访问一个URL不含有变量的时候,映射到视图函数 并且视图函数的形参有一个默认参数含有值,视图函数就直 接使用这个默认参数。 2.当用户访问一个URL含有变量的时候,映射到视图函数并 且视图函数的形参有一个默认参数含有值,视图函数就不使 用默认参数的值,而是采用URL匹配规则传进来的值。 总结:其实上面的默认函数使用规则和编程语言函数的默认 参数使用规则类似。

转换器

前一节我们简单使用了int转换器将同级的两种传递参数的方式区分开来,不然查询字符串方式会被第一种传递参数方式所捕获。 我们的转换器是用在path函数中,所以有必要简单介绍下path函数,这里只简单介绍第一个参数,下一篇文章会详细讲path函数的使用方法。 path函数签名如下:

path(route,view,name=None,kwargs=None)  

route 参数: url 的匹配规则。这个参数中可以指定 url 中需要传递的参数,比如在访问文章详情页的时候,可以传递一个 id 。传递参数是通过 <> 尖括号来进行指定的。并且在传递参数的时候,可以指定这个参数的数据类型,比如文章的 id 都是 int 类型,那么可以这样写 int:id ,以后匹配的时候,就只会匹配到 id 为 int 类型的 url ,而不会匹配其他的 url ,并且在视图函数中获取这个参数的时候,就已经被转换成一个 int 类型了。 其中还有几种常用的转换器类型: 1.str:非空的字符串类型。默认的转换器。但是不能包含斜杠。 2.int:匹配任意的零或者正数的×××。到视图函数中就是一个int类型。 3.slug:由英文中的横杠 - ,或者下划线 _ 连接英文字符或者数字而成的字符串。 4.uuid:匹配 uuid 字符串。 5.path:匹配非空的英文字符串,可以包含斜杠。

Tips:通过from django.urls import converters可以导入定义转换器的模块。

下面重点介绍int、str、uuid转换器,而slug和path转换器不再解释。

str转换器

str转换器是默认的转换器,代表如果在参数前面不添加任何东西,那么将默认采用str转换器。 在converters模块中对str转换器的定义如下图: 其实说白了也就是一个类,然后定义了一个正则表达式,对传递的参数进行正则匹配。

int转换器

在converters模块中对int转换器的定义如下图: 从类的定义中可以看出,它是匹配一个0-9的任意一个或多个以上的数字。

uuid转换器

UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,每个人都可以创建不与其它人冲突的UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问题。目前最广泛应用的UUID,是微软公司的全局唯一标识符(GUID)。全局唯一标识符(GUID,Globally Unique Identifier)是一种由算法生成的二进制长度为128位的数字标识符。GUID的总数达到32^128个,所以随机生成两个相同GUID的可能性极小,但并不为0。GUID的格式为:"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",其中每个 x 是 0-9 或 a-f 范围内的一个4位十六进制数,所以每个x代表4个二进制位,所以4*32=128个二进制位,即GUID的总数达到32^128个。例如:6F9619FF-8B86-D011-B42D-00C04FC964FF 即为有效的 GUID 值。 在converters模块中对uuidt转换器的定义如下图:

URL中包含另外一个urls模块

在我们的项目中,不可能只有一个 app ,如果把所有的 app 的 views 中的视图都放在 first_project项目的urls.py 中进行映射,肯定会让代码显得非常乱。 就像我们目前的项目一样显得十分混乱,多个book的视图映射都放在一起,如下图: 因此 django 给我们提供了一个方法,可以在 app 内部包含自己的 url 匹配规则,而在项目的 urls.py 中再统一包含这个 app 的 urls 。使用这个技术需要借助 include 函数,对include函数的使用下一篇会有详细讲解,这里只要知道它的作用即可。 好,现在my_app包里添加一个urls.py文件,代码修改如下:

from django.urls import path
from my_app import views
from django.urls import converters

urlpatterns = [
    path('', views.book_list),
    path('<int:book_id>/', views.book_detail),  # <book_id>就是参数
    path('pub/', views.book_pub)
]

然后将first_project项目的urls.py代码修改如下:

from django.contrib import admin
from django.urls import path,include
from my_app import views
from django.urls import converters

urlpatterns = [
    path('admin/', admin.site.urls),
    path('book/', include("my_app.urls"))
]

从上面两个文件代码的修改我们可以这样理解:现在first_project项目的urls.py文件,将’book/‘原本的映射交给了my_app包里的urls.py文件进行内部映射。其实这两个文件之间的映射关系就相当于字符串的拼接,例如:'book/'拼接my_app的urls.py文件的'pub/'组合成’book/pub/‘,然后再映射到book_pub视图函数。

运行结果如下,完全没有问题,跟分离前一模一样: