Django 系列博客(六)
Django 系列博客(六)
前言
本篇博客介绍 Django 中的路由控制部分,一个网络请求首先到达的就是路由这部分,经过路由与视图层的映射关系再执行相应的代码逻辑并将结果返回给客户端。
Django 中路由的作用
URL 配置(URLconf)可以比作是 Django 支撑网站的目录。它的本质是 URL 要为该 URL 滴啊用的视图函数之间的映射表。以这种方式告诉 Django,对于客户端发来的 URL 要具体调用视图层的哪段代码。
from django.urls import url
from app import views
urlpatterns = [
url(r'^$', views.home),
]
# ^$这个路由对应视图函数中的 home 方法,只要浏览器往该网址发送请求,就会响应到这个函数执行。
简单的路由配置
from django.conf.urls import url
urlpatterns = [
url(正则表达式, views视图函数, 默认参数, 路由别名),
]
- 正则表达式:一个正则表达式字符串;
- views 视图函数:一个可调用对象,通常为一个视图函数或一个指定视图路径的字符串;
- 默认参数:可选的要传递给视图函数的默认参数(字典形式);
- 路由别名:一个可选的name 参数。
from django.conf.urls import url
from app import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^article/(\d{4})/(\d{2})', views.article),
url(r'^user/(?P<name>[a-z]{3})', views.user_havename),
url(r'^user/$', views.user),
]
注意:
- 若要从 URL 中捕获一个值,只需要在它周围放置一对圆括号;
- 不需要添加一个前导的反斜杠,因为每个 url 都有。例如,应该是
^articles
而不是^/articles
。 - 每个正则表达式前面的
r
是可选的,但是建议加上,表示这是个原生字符串,字符串中的任何字符都不应该转义。 - urlpatterns 中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续。
一些请求的例子:
/articles/2005/03/ 请求将匹配列表中的第三个模式。Django 将调用函数views.month_archive(request, '2005', '03')。
/articles/2005/3/ 不匹配任何URL 模式,因为列表中的第三个模式要求月份应该是两个数字。
/articles/2003/ 将匹配列表中的第一个模式不是第二个,因为模式按顺序匹配,第一个会首先测试是否匹配。请像这样自由插入一些特殊的情况来探测匹配的次序。
/articles/2003 不匹配任何一个模式,因为每个模式要求URL 以一个反斜线结尾。
/articles/2003/03/03/ 将匹配最后一个模式。Django 将调用函数views.article_detail(request, '2003', '03', '03')。
APPEND_SLASH
该参数的作用是会在访问连接的时候,如果连接没有加斜杠,会自动加上。可以在global_settings.py
中修改。
项目的 settings 中没有改配置的选项,一个项目会有两个 settings。
效果:
from django.conf.urls import url
from app_01 import views
urlpatterns = [
url(r'^blog/$', views.blog),
]
当我们访问http://www.example.com/blog
时,默认将网址自动替换成http://www.example.com/blog/
。如果将该配置值设为false
,此时在向该网址发送请求时就会提示找不到页面。
分组
当我们需要捕获 url 中的参数并要传递给视图函数时,有两种捕获方式:无名分组和有名分组。无名分组就是该值没有变量标识,传值的时候采用位置参数传递;有名分组就是给捕获的值赋值一个变量,这样就可以通过关键字参数传值了。
无名分组
# urls.py文件
from django.conf.urls import url
from app_01 import views
urlpatterns = [
url(r'^article/(\d{4})/(\d{2})', views.article),
]
# views.py文件
from django.shortcuts import HttpResponse
def article(reques, year, month):
return HttpResponse('您要查看%s 年%s 月的文章' %(year, month))
通过圆括号捕获年份和月份,然后通过位置参数传递给 year 和 month 变量,接着在 views 函数中使用。
有名分组
import re
ret=re.search('(?P<year>[0-9]{4})/([0-9]{2})','2012/12')
print(ret.group())
print(ret.group(1))
print(ret.group(2))
print(ret.group('year'))
这些示例使用简单的、没有命名的正则表达式组(通过圆括号)来捕获 URL 中的值并以位置参数传递给视图。在更高级的用法中,可以使用命名的正则表达式组来捕获 URL 中的值并以关键子参数传递给视图函数。
在python 正则表达式中,命名有名分组的语法时(P<变量名>pattern)
,其中变量名是分组的标识符,pattern 是要匹配的正则表达式。下面是上面 URLconf 使用有名分组的重写:
from django.urls import path, re_path
from app_01 import views
urlpatterns = [
re_path(r'^articles/2003/$', views.special_case_2003),
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
re_path(r'^articles/(?P<year[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
re_path(r'^articles/(?P<year[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.archive_detail),
]
# 捕获到的数据都是 str 类型
# 视图函数里可以指定默认值
url('blog/$', views.blog),
url('blog/?(?P<num>[0-9]{1})', views.blog),
def blog(request, num=1):
print(num)
return HttpResponse('ok')
这个实现与前面的示例完全相同,只有一个细微的差别:捕获的值作为关键字参数而不是位置参数传递给视图函数。例如:
/articles/2005/03/ 请求将调用views.month_archive(request, year='2005', month='03')函数,而不是views.month_archive(request, '2005', '03')。
/articles/2003/03/03/ 请求将调用函数views.article_detail(request, year='2003', month='03', day='03')。
在实际应用中,这意味你的 URLconf 会更加清晰且不容易产生参数顺序问题的错误,可以在视图函数定义中重新安排参数的顺序。
路由分发
其实和项目名相同的文件夹下面的urls.py
文件是整个项目的根路由:
这是整个项目的根路由,所有向该项目发送的连接请求,首先需要从该路由配置里面过滤,如果只有一个应用,或者路由配置不多一个根路由就足够了,但当 app 多起来之后还是使用一个根路由配置会造成路由混乱,所以有了路由分发,比如这是发往app_01
应用的连接,那么在根路由中进行路由分发,把连接转向app_01
中的路由中进行处理,这就是路由分发。
# 根路由配置
from django.conf.urls import url, include
from app_01 import views
from app_01 import urls
urlpatterns = [
url(r'^app_01/', include('app_01.urls')), # 注意前面的正则表达式后面不能加$
url(r'^app_01/', include(urls)),
]
# 应用路由配置
from django.conf.urls import url
from app_01 import views
urlpatterns = [
url(r'^test/(?P<year>[0-9]{2})/$', views.url_test),
]
反向解析
在使用 Django 项目时,一个常见的需求是获得 URL 的最终形式,以用于嵌入到生成的内容中(视图中和显示给用户的 URL 等)或者用于处理服务器端的导航(重定向等)。人们强烈希望不要硬编码这些 URL(费时费力、不可扩展且易产生错误)或者设计一种与 URLconf 毫不相关的专门的 URL 生成机制,因为这样容易导致一定程度上产生过期的 URL。
在需要 URL 的地方,对于不同层级,Django 提供不同的工具用于 URL 反查:
- 在模板中:使用 url 模板标签;
- 在 python 代码中:使用
from django.urls import reverse
。
# urls.py文件
from django.urls import path, re_path
from app_01 import views
urlpatterns = [
re_path(r'^test/(?P<year>[0-9]{2})/(P?<month>[0-9]{2})/$', views.url_test, name='test'),
]
<!-- html文件 -->
<a href="{% url 'test' 10 23 %}">哈哈</a>
# views.py文件
from django.shortcuts import render, HttpResponse, redirect, reverse
def url_test(request, year, month):
url = reverse('test', args=(10, 20))
return HttpResponse('ok')
总结:
- 在 html 代码里面使用{% url '别名' 参数 参数... %};
- 在视图函数中:
url = reverse('test')
url = reverse('test', args=(10, 20))
当命名 URL 模式时,要确保使用的名称不会与其他应用中的名称产生冲突。如果你的 URL 模式叫做 comment,而另外一个应用中也有一个同样的名字,当你在模板中使用这个名称的时候不能保证将插入哪个 URL。在 URL 名称加上一个前缀,比如应用的名称,将会减少冲突的可能。建议使用myapp-comment
。
名称空间
命名空间(namespace)是表示标识符的可见范围。一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。这样在一个新的命名空间中可以定义任何标识符,它们不会与任何已有的标识符产生冲突,因为已有的定义处于其他的命名空间中。
由于name
没有作用域,Django 在返解 URL 时,会在项目的全局路径中按顺序搜索,当查找到第一个name
指定的 URL 时,立即返回。
在开发项目时,会经常使用name
属性反解出 URL,当不小心在不同的 app 的 urls 中定义相同的name
时,可能会导致 URL 反解错误,为了避免发生引入了命名空间。
创建 app01和 app02
python manage.py startapp app01
python manage.py startapp app02
根路由
from django.urls import path,re_path,include
urlpatterns = [
path('app01/', include('app01.urls')),
path('app02/', include('app02.urls'))
]
app01的 urls.py
from django.urls import path,re_path
from app01 import views
urlpatterns = [
re_path(r'index/',views.index,name='index'),
]
app02的 urls.py
from django.urls import path, re_path, include
from app02 import views
urlpatterns = [
re_path(r'index/', views.index,name='index'),
]
app01的视图
def index(request):
url=reverse('index')
print(url)
return HttpResponse('index app01')
app02的视图
def index(request):
url=reverse('index')
print(url)
return HttpResponse('index app02')
这样都找index.html
,app01和 app02找到的都是app02的 index。如何处理?在路由分发的时候指定名称空间。根路由在路由分发是,指定名称空间。
# 一
path('app01/', include(('app01.urls','app01'))),
path('app02/', include(('app02.urls','app02')))
# 二
url(r'app01/',include('app01.urls',namespace='app01')),
url(r'app02/',include('app02.urls',namespace='app02'))
# 三
url(r'app01/',include(('app01.urls','app01'))),
url(r'app02/',include(('app02.urls','app02')))
在视图函数反向解析的时候,指定名称空间:
url=reverse('app02:index')
print(url)
url2=reverse('app01:index')
print(url2)
在模板里面也是用相应的名称空间名:
<a href="{% url 'app02:index'%}">哈哈</a>
Django2.0版的 path
Django2.0的re_path
和1.0的url
一样。2.0多了个path
思考情况如下:
urlpatterns = [
re_path('articles/(?P<year>[0-9]{4})/', year_archive),
re_path('article/(?P<article_id>[a-zA-Z0-9]+)/detail/', detail_view),
re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/edit/', edit_view),
re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/delete/', delete_view),
]
考虑下这样的两个问题:
第一个问题,函数 year_archive
中year参数是字符串类型的,因此需要先转化为整数类型的变量值,当然year=int(year)
不会有诸如如TypeError或者ValueError的异常。那么有没有一种方法,在url中,使得这一转化步骤可以由Django自动完成?
第二个问题,三个路由中article_id都是同样的正则表达式,但是你需要写三遍,当之后article_id规则改变后,需要同时修改三处代码,那么有没有一种方法,只需修改一处即可?
在Django2.0中,可以使用 path
解决以上的两个问题。
基本示例
from django.urls import path
from . import views
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<int:year>/', views.year_archive),
path('articles/<int:year>/<int:month>/', views.month_archive),
path('articles/<int:year>/<int:month>/<slug>/', views.article_detail),
# path才支持,re_path不支持
path('order/<int:year>',views.order),
]
基本规则:
- 使用尖括号(<>)从 url中捕获值;
- 捕获值中可以包含一个转化器类型(converter type),比如使用
<int:name>
捕获一个整数变量。如果没有转化器,将匹配任何字符串,也包含/
字符。 - 无需添加前导斜杠
以上是示例,分别和上面的基本示例对应:
path 转换器
- str:匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式;
- int:匹配正整数,包含0;
- slug:匹配字母、数字、横杠以及下划线组成的字符串;
- uuid:匹配格式化的
uuid
,如075194d3-6885-417e-a8a8-6c931e272f00。 - path: 匹配任何非空字符,包含了路径分隔符(/)
注册自定义转化器
对于一些复杂或者复用的需要,可以定义自己的转化器。转化器是一个类或接口,它的要求有三点:
- regex类属性,字符串类型;
- to_python(self, value)方法,value 是由类属性 regex 所匹配到的字符串,返回具体的 python 变量值,以供 Django 传递到对应的视图函数中;
- to_url(self, value)方法,和to_python相反,value 是一个具体的 python 变量值,返回其字符串,通常用于 url 反向引用。
示例
class FourDigitYearConverter:
regex = '[0-9]{4}'
def to_python(self, value):
return int(value)
def to_url(self, value):
return '%04d' % value
使用register_converter
将其注册到 URL 配置中:
from django.urls import register_converter, path
from . import converters, views
register_converter(converters.FourDigitYearConverter, 'yyyy')
urlpatterns = [
path('articles/2003/', views.spwcial_case_2003),
path('articles/<yyyy:year>/', views.year_archive),
]
Django 系列博客(六)的更多相关文章
- Django 系列博客(十六)
Django 系列博客(十六) 前言 本篇博客介绍 Django 的 forms 组件. 基本属性介绍 创建 forms 类时,主要涉及到字段和插件,字段用于对用户请求数据的验证,插件用于自动生成 h ...
- Django 系列博客(十四)
Django 系列博客(十四) 前言 本篇博客介绍在 html 中使用 ajax 与后台进行数据交互. 什么是 ajax ajax(Asynchronous Javascript And XML)翻译 ...
- Django 系列博客(十三)
Django 系列博客(十三) 前言 本篇博客介绍 Django 中的常用字段和参数. ORM 字段 AutoField int 自增列,必须填入参数 primary_key=True.当 model ...
- Django 系列博客(十二)
Django 系列博客(十二) 前言 本篇博客继续介绍 Django 中的查询,分别为聚合查询和分组查询,以及 F 和 Q 查询. 聚合查询 语法:aggregate(*args, **kwargs) ...
- Django 系列博客(十一)
Django 系列博客(十一) 前言 本篇博客介绍使用 ORM 来进行多表的操作,当然重点在查询方面. 创建表 实例: 作者模型:一个作者有姓名和年龄. 作者详细模型:把作者的详情放到详情表,包含生日 ...
- Django 系列博客(十)
Django 系列博客(十) 前言 本篇博客介绍在 Django 中如何对数据库进行增删查改,主要为对单表进行操作. ORM简介 查询数据层次图解:如果操作 mysql,ORM 是在 pymysql ...
- Django 系列博客(九)
Django 系列博客(九) 前言 本篇博客介绍 Django 模板的导入与继承以及导入导入静态文件的几种方式. 模板导入 模板导入 语法:``{% include '模板名称' %} 如下: < ...
- Django 系列博客(八)
Django 系列博客(八) 前言 本篇博客介绍 Django 中的模板层,模板都是Django 使用相关函数渲染后传输给前端在显式的,为了想要渲染出我们想要的数据,需要学习模板语法,相关过滤器.标签 ...
- Django 系列博客(七)
Django 系列博客(七) 前言 本篇博客介绍 Django 中的视图层中的相关参数,HttpRequest 对象.HttpResponse 对象.JsonResponse,以及视图层的两种响应方式 ...
随机推荐
- Java Web程序开发链接MySQL数据库
显示错误:Access denied for user ''@'localhost' (using password: YES) 保证URL中没有空格 尝试用MySQL本地命令行登陆 显示错误:The ...
- Leetcode35 Search Insert Position 解题思路(python)
本人编程小白,如果有写的不对.或者能更完善的地方请个位批评指正! 这个是leetcode的第35题,这道题的tag是数组,python里面叫list,需要用到二分搜索法 35. Search Inse ...
- iOS TouchID & FaceID
import UIKit import LocalAuthentication //指纹识别必须用真机测试,并且在iOS8以上系统,如果是FaceID至少IOS11以上. class Authenti ...
- 【转载】关于.NET下开源及商业图像处理(PSD)组件
原创]关于.NET下开源及商业图像处理(PSD)组件 阅读目录 1 前言 2 .NET图像处理组件总结 3.相关资源网址 本博客所有文章分类的总目录:http://www.cnblo ...
- VSCode插件开发全攻略(六)开发调试技巧
更多文章请戳VSCode插件开发全攻略系列目录导航. 前言 在介绍完一些比较简单的内容点之后,我觉得有必要先和大家介绍一些开发中遇到的一些细节问题以及技巧,特别是后面一章节将要介绍WebView的知识 ...
- HTML页面中插入CSS样式的三种方法
1. 外部样式 当样式需要应用于很多页面时,外部样式表将是理想的选择.在使用外部样式表的情况下,你可以通过改变一个文件来改变整个站点的外观.每个页面使用<link>标签链接到样式表. &l ...
- 【Spark调优】小表join大表数据倾斜解决方案
[使用场景] 对RDD使用join类操作,或者是在Spark SQL中使用join语句时,而且join操作中的一个RDD或表的数据量比较小(例如几百MB或者1~2GB),比较适用此方案. [解决方案] ...
- PyTorch-Adam优化算法原理,公式,应用
概念:Adam 是一种可以替代传统随机梯度下降过程的一阶优化算法,它能基于训练数据迭代地更新神经网络权重.Adam 最开始是由 OpenAI 的 Diederik Kingma 和多伦多大学的 Jim ...
- dpkg: 处理软件包 qjackctl (--configure)时出错解决方法
第一步:备份 $ sudo mv /var/lib/dpkg/info /var/lib/dpkg/info.bk 第二步:新建 $ sudo mkdir /var/lib/dpkg/info 第三步 ...
- MySQL单表多字段模糊查询
今天工作时遇到一个功能问题:就是输入关键字搜索的字段不只一个字段,比如 我输入: 超天才 ,需要检索出 包含这个关键字的 name . company.job等多个字段.在网上查询了一会就找到了答案. ...