前沿
上一节的学前准备工作和第一个小牛试刀的 Django 项目学习, 让我们对 Django 开发越来越感兴趣了. 正所谓趁热打铁, 让我们继续来学习网站开发必备的视图函数和 URL 映射等知识, 跟着步伐一起学习, 我相信你会收获很多. 噔~ 噔, 新闻播报时间: 9 月 12-9 月 16 有两个超强台风在广东湛江登 6, 沿途影响众多城市, 包括深圳!! 所以很遗憾, 前几天打算和舍友去深圳游玩的计划泡汤了, 也只能推迟几天前往深圳度中秋, 看月亮了~
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> https://www.jianshu.com/p/ ; , 其中 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/page<int: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: 匹配任意的 0 或者正数的 ***. 到视图函数中就是一个 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 视图函数.
运行结果如下, 完全没有问题, 跟分离前一模一样:
来源: http://blog.51cto.com/12731497/2174887