前沿
上一篇文章我们学习了 URL 与视图函数的映射, 传递参数的三种方式, 转换器的简单使用和 include 函数分层映射管理. 接下来这一篇文章着重介绍 Path,re_path,include,reverse,redirect 函数的使用和自定义 URL 转换器. 学完这些内容, 相信我们对 URL 和视图都会有了一定的了解和认识. 为了让每篇文章具有一定的独立性, 我决定每篇文章都重新新建一个项目, 便于测试和调试.
预备
首先, 我们在 Pycharm 重新新建一个名为 book_project 的 Django 项目, 然后打开 CMD 窗口进入虚拟环境, 接着跳转到该项目的目录下(含有 manage.py), 再依次执行创建 app 命令, 如下图:
创建完成后, 该项目的样子大概是酱紫的:
模拟前端和后端
我们想实现: front 前端拥有前端首页和前端登 6 页面, 而 cms 后端拥有后端首页和后端登 6 页面, 然后分别输入配置好的 URL 对这四个页面进行访问.
首先, 为 cms 和 front 两个 app 手动添加 urls.py 文件. 然后分别按以下步骤添加或修改项目文件代码:
(1)为 cms 的 urls.py 文件添加以下代码:
- from django.urls import path
- from . import views
- urlpatterns = [
- path("", views.index),
- path("login/", views.login)
- ]
(2)为 front 的 urls.py 文件添加以下代码:
- from django.urls import path
- from . import views
- urlpatterns = [
- path("", views.index),
- path("login/", views.login)
- ]
(3)为 cms 的 views.py 文件添加以下代码:
- from django.http import HttpResponse
- def index(request):
- return HttpResponse("后端首页")
- def login(request):
- return HttpResponse("后端登 6 页面")
(4)为 front 的 views.py 文件添加以下代码:
- from django.http import HttpResponse
- def index(request):
- return HttpResponse("前端首页")
- def login(request):
- return HttpResponse("前端登 6 页面")
(5)为 book_project 这个主包的 urls.py 文件添加以下代码:
- from django.urls import path,include
- urlpatterns = [
- path('', include("front.urls")),
- path('cms/', include("cms.urls"))
- ]
如果学习了上一篇文章的内容, 我相信这些代码的编写是完全 OK 的. 好, 我们运行一下项目(注意, 我们是新建的项目, 记得在勾选单一实例运行.).
运行结果如下(完美, 成功运行~):
实际需求
模拟自动跳转新用户登 6 页面
实际需求: 要想实现一个类似于知乎主页一样, 若是新用户第一次登 6 知乎, 不管哪个页面都会跳转到登 6 账号的页面, 对于这一需求, 我们可以通过以下模拟实现(为了更好观察代码, 一样的代码部分我直接提示省略):
(1)修改 front 前台的 index 视图函数
- # 省略上面代码
- from django.shortcuts import redirect # 导入重定向 redirect 函数, 最好记住 redirect 函数源于哪个模块, 因为要经常重定向.
- def index(request):
- # ?username=xxx
- username = request.GET.get('username') # 当用户输入 127.0.0.1:8000, 检测到没有账户则跳转到注册页面, 当输入 http://127.0.0.1:8000/?username=1 正常显示前台首页.
- if username:
- return HttpResponse('前台首页')
- else:
- return redirect('/login/') # 跳转通过 redirect 函数来进行页面重定向, 重新访问指定的 url 页面
- # 省略下面代码
修改完代码后, 按下 Ctrl+S 保存(自动重新编译项目). 注意观察, 输入 127.0.0.1:8000 会发现自动跳转到 127.0.0.1:8000/login/.
重定向代码的缺陷
其实还存在这样一种情况, 假如老板或项目经理突然脑袋秀逗了, 要求你更改网页的 URL, 将 127.0.0.1:8000/login / 更改为 127.0.0.1:8000/signin/, 假如这时还采用上面的代码进行网页重定向, 那你会非常抓狂, 举我们这个项目栗子来说, 不单只要修改为下面这样:
- urlpatterns = [
- path("", views.index),
- path("signin/", views.login)
- ]
还要修改重定向位置的代码这样:
- def index(request):
- # ?username=xxx
- username = request.GET.get('username') # 当用户输入 127.0.0.1:8000, 检测到没有账户则跳转到注册页面, 否则正常显示前台首页.
- if username:
- return HttpResponse('前台首页')
- else:
- return redirect('/singin/') # 跳转通过 redirect 函数来进行页面重定向, 重新访问指定的 url 页面
如果公司网站项目十分庞大的话, 那可能还有很多地方都要修改, 这十分耗时也费力. 所以, 我们推荐为 URL 命名来解决这一问题, 给 URL 取个名字, 只要调用 reverse 反转 URL 的名字而不是直接重定向写死的 URL, 那么无论 URL 怎么修改也影响不到其他地方.
Path 函数
在学习 URL 命名之前, 先详细学习下 Path 函数的使用.
path 函数的定义为:
path(route,view,name=None,kwargs=None) .
以下对这几个参数进行讲解.
route 参数: url 的匹配规则. 这个参数中可以指定 url 中需要传递的参数, 上一篇文章已讲解, 这个参数不再赘述. 请移步到谈谈 Python 之 Django 搭建企业级官网(第三篇上部) http://blog.51cto.com/12731497/2174887
view 参数: 可以为一个视图函数或者是 类视图. as_view() 或者是 django.urls.include() 函数的返回值.
name 参数: 这个参数是给这个 url 取个名字的, 这在项目比较大, url 比较多的时候用处很大. 这个参数就是用于 URL 命名.
kwargs 参数: 有时候想给视图函数传递一些额外的参数, 就可以通过 kwargs 参数进行传递. 这个参数接收一个字典. 会将 foo=bar 作为关键字实参传入视图函数, 所以视图函数要有形参来接收实参.
比如以下的 url 规则:
- from django.urls import path
- from . import views
- urlpatterns = [
- path('blog/<int:year>/', views.year_archive, kwargs={'foo':'bar'}),
- ]
那么以后在访问 blog/1991/ 这个 url 的时候, 会将 {'foo':'bar'} 作为关键字参数传给 year_archive 函数. year_archive 视图函数的形参中最后添加一个 kwargs 参数来接收这个额外的参数.
URL 命名
学习完 path 函数各参数, 相信都知道该函数的 name 参数就是用于 URL 命名的了.
接下来修改 front 包的 urls.py 文件代码如下:
- urlpatterns = [
- path("", views.index),
- path("login/", views.login, name='login')
- ]
再次修改 front 包的 views.py 文件代码如下:
- # 省略上面代码
- from django.shortcuts import redirect, reverse # 注意这里添加了 reverse 函数, reverse 函数用于将指定 URL 名字反转成 URL.
- def index(request):
- # ?username=xxx
- username = request.GET.get('username') # 当用户输入 127.0.0.1:8000, 检测到没有账户则跳转到注册页面, 否则正常显示前台首页.
- if username:
- return HttpResponse('前台首页')
- else:
- # reverse('login')函数返回'/login/'
- return redirect(reverse('login')) # 跳转通过 redirect 函数来进行页面重定向, 重新访问指定的 url 页面(相当于重新访问 127.0.0.1:8000/login/)
- # 省略下面代码
按下 Ctrl+S 保存, 输入 127.0.0.1:8000 成功跳转到 127.0.0.1:8000/login 登 6 页面.
应用命名空间
在公司实际开发中, 如果公司里的多个人同时负责网站的开发, 而且 A 同事负责开发 front 的 app,B 同事负责开发 cms 的 app, 那么由于两个 app 都有首页和登 6 页面, 那么有可能 url 的 name 属性可能会相同冲突. 在多个 app 之间, 有可能产生同名的 url, 这时候为了避免反转 url 的时候产生混淆, 可以使用应用命名空间来做区分. 定义应用命名空间非常简单, 只要在 "app" 的 "urls.py" 中定义一个叫做 "app_name" 的变量, 来指定这个应用的命名空间即可.
就好比我们的项目, 将 front 包里的 urls.py 和 views.py 文件和 cms 包里的 urs.py 和 views.py 文件分别为 URL 映射命名为 index 和 login, 如下图所示:
运行项目, 输入 127.0.0.1:8000 自动跳转到 127.0.0.1:8000/cms/login 网页, 而不是之前的 127.0.0.1:8000/login 网页, 由于多个 app 的 URL 拥有相同的名字, 所以 Django 在执行 reverse 函数反转 URL 时懵逼了. 为了解决这个问题, 我们将采用应用命名空间.
修改 front 包的 urls.py 文件的代码如下(django 在执行到 app 时, 会自动读取这个应用命名空间并将这个名字作为 app 的名字):
- from django.urls import path
- from . import views
- app_name = "front" # 添加了应用命名空间
- urlpatterns = [
- path("", views.index, name="index"),
- path("login/", views.login, name='login')
- ]
以后在做反转的时候就可以使用 "应用命名空间: url 名称" 的方式进行反转. 示例代码如下修改 front 包的 views.py 文件的代码如下:
- def index(request):
- # ?username=xxx
- username = request.GET.get('username') # 当用户输入 127.0.0.1:8000, 检测到没有账户则跳转到注册页面, 否则正常显示前台首页.
- if username:
- return HttpResponse('前台首页')
- else:
- return redirect(reverse('front:login')) # 这里为 URL 名字前面添加 front 应用命名空间名
按下 Ctrl+S 保存, 输入 127.0.0.1:8000 自动跳转到 127.0.0.1:8000/login 网页, 成功了, Django 不会再懵逼了.
include 函数
在上一篇文章我们知道, 在项目不断庞大以后, 经常不会把所有的 url 匹配规则都放在项目的 urls.py 文件中, 而是每个 app 都有自己的 urls.py 文件, 在这个文件中存储的都是当前这个 app 的所有 url 匹配规则. 然后再统一 include 到项目的 urls.py 文件中. include 函数有多种用法, 这里讲下几种常用的用法:
(1)include(pattern,namespace=None) : 直接把其他 app 的 urls 包含进来.
之前的 include 用法, 举个栗子(下面的代码仅用于解释用法, 不是将代码添加到项目):
- from django.contrib import admin
- from django.urls import path,include
- urlpatterns = [
- path('admin/', admin.site.urls),
- path('book/',include("book.urls"))
- ]
我们可以发现这一种用法其实该函数还有参数 2 指定实例命名空间, 默认是 None. 但是在使用实例命名空间之前, 必须先指定一个应用命名空间, 不先指定应用命名空间会报错. 一个 app 可以创建多个实例, 也就是可以使用多个 url 映射同一个 app, 所以这就产生一个问题. 以后在做反转的时候, 如果使用应用命名空间, 那么就会发生混淆.
将 book_project 主包的 urls.py 文件代码修改如下:
- from django.urls import path,include
- urlpatterns = [
- path('', include("front.urls")),
- path('cms1/', include("cms.urls")), # 这两行代码代表输入 127.0.0.1:8000/cms1 或 127.0.0.1:8000/cms2 能映射到 cms.urls 内部的 url 路径.
- path('cms2/', include("cms.urls"))
- ]
为 cms 包的 urls.py 文件添加应用命名空间:
app_name = "cms"
为 cms 包的 views.py 文件修改代码如下:
- def index(request):
- # ?username=xxx
- username = request.GET.get('username') # 当用户输入 127.0.0.1:8000, 检测到没有账户则跳转到注册页面, 否则正常显示前台首页.
- if username:
- return HttpResponse('后台首页')
- else:
- return redirect(reverse('cms:login')) # 这里使用了应用命名空间反转 URL
经过上面代码的修改, 这时候按下 Ctrl+S 保存, 输入 127.0.0.1:8000/cms1 成功跳转到 127.0.0.1:8000/cms1/login, 但是当输入 127.0.0.1:8000/cms2 却自动跳转到了 127.0.0.1:8000/cms1/login,what? 我的 127.0.0.1:8000/cms2/login 哪里去了? 导致上面的原因就是前面介绍的一个 app 可以创建多个实例, 也就是可以使用多个 url 映射同一个 app, 所以这就产生一个问题. 以后在做反转的时候, 如果使用应用命名空间, 那么就会发生混淆. 为了避免这个问题, 我们可以使用实例命名空间. 实例命名空间也是非常简单, 只要在 "include" 函数中传递一个 "namespace" 变量即可.
在上面代码的基础上继续修改代码, 修改 cms 包的 views.py 文件代码如下:
- def index(request):
- username = request.GET.get('username')
- if username:
- return HttpResponse("后端首页")
- else:
- current_namespace = request.resolver_match.namespace # 返回当前 app 对应的实例命名空间(cms1 或 cms2)
- return redirect(reverse('%s:login' % current_namespace)) # 相当于 "cms1:login" 或 "cms2:login"
接着修改 book_project 主包的 urls.py 文件代码如下:
- from django.urls import path,include
- urlpatterns = [
- path('', include("front.urls")),
- path('cms1/', include("cms.urls", namespace="cms1")), # 使用了实例命名空间 namespace
- path('cms2/', include("cms.urls", namespace="cms2"))
- ]
这时候按下 Ctrl+S 保存, 输入 127.0.0.1:8000/cms1 成功跳转到 127.0.0.1:8000/cms1/login, 然后输入 127.0.0.1:8000/cms2 也成功跳转到了 127.0.0.1:8000/cms2/login 页面. 如下图:
(2)include((pattern_list,app_namespace),namespace=None) :"include" 函数的第一个参数既可以作为一个字符串, 也可以作为一个元组, 如果是元组, 那么元组的第一个参数是子 "urls.py" 模块的字符串, 元组的第二个参数是应用命名空间, 也就是说应用命名空间既可以在子 "urls.py" 种通过 "app_name" 指定, 也可以在 "include" 函数中指定.
将 book_project 主包的 urls.py 文件代码修改如下:
- from django.urls import path, include
- from front import views
- urlpatterns = [
- path('', include(([
- path('', views.index, name="index"),
- path("login/", views.login, name='login')
- ], "front"))), # 注意
- path('cms1/', include("cms.urls", namespace="cms1")),
- path('cms2/', include("cms.urls", namespace="cms2"))
- ]
上面的代码相当于完全忽略了 front 包的 urls.py 文件的作用, 因为已经被 include 函数的第一个参数列表给替代了, 所以 urls.py 文件的 "app_name ="front"" 指定的应用命名空间自然也失效了. 这时运行代码完全 OK, 跟之前的一摸一样, 输入 127.0.0.1:8000 自动跳转到 127.0.0.1:8000/login 页面.
(3)include(pattern_list) : 可以包含一个列表或者一个元组, 这个元组或者列表中又包含的是 path 或者是 re_path 函数(该函数后面会讲).
这个函数跟上一个函数差不多, 也是用含有 path 函数的列表或元组替代了之前的 pattern. 但是需要注意的是, 因为这样会忽略了 front 包的 urls.py 文件的作用, 所以 urls.py 文件的 "app_name ="front"" 指定的应用命名空间自然也失效了. 那么如果你映射的视图函数内部进行反转 URL 时指定了应用命名空间, 那么将会报错, 会提示找不到 front 命名空间, 如下:
所以, 综上建议, 如果你映射的视图函数内部进行反转 URL 时指定了应用命名空间, 最好调用 include((pattern_list,app_namespace),namespace=None) 函数, 在指定列表或元组 的同时也指定应用命名空间.
后面的内容明天补充.....
谈谈 Python 之 Django 搭建企业级官网(第三篇下部)
来源: http://www.bubuko.com/infodetail-2769567.html