Django – 路由系统

  • A+
所属分类:Python教程 学编程

一、说明

Django 1.11版本 URLconf官方文档
Django 2.0版本URLconf官方文档

1.1、路由系统说明

URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表。

你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码。

1.2、基本格式

1.Django 1.11基本格式

from django.conf.urls import url

urlpatterns = [
     url(正则表达式, views视图函数,参数,别名),
]

2.Django 2.0版本中的路由系统已经替换成下面的写法
其实2.0版本除了将url关键字改为path之外其他的变动并不大.

from django.urls import path

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:slug>/', views.article_detail),
]

参数说明:

  • 正则表达式:一个正则表达式字符串

  • views视图函数:一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串

  • 参数:可选的要传递给视图函数的默认参数(字典形式)

  • 别名:一个可选的name参数

二、URL中的正则表达式详解

2.1、基本配置

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),          #^articles/2003/$:表示以articles/开头并且以2013/结尾(两级(/a/b)固定的URL)
    url(r'^articles/([0-9]{4})/$', views.year_archive),         #^articles/([0-9]{4})/$:表示以^articles/开头,后面是0-9之间的任意四个数字组成的url结尾(两级/a/0-9/可变的url)
    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),              #^articles/([0-9]{4})/([0-9]{2})/$:表示以^articles/开头,中间为0-9之间任意数组成的四个数字,最后是0-9之间任意两个数字组成的URL结尾(三层(/a/0-9/0-9)可变的URL),这里要注意,由于URL分组了,URL对应的函数除了默认接收一个request参数外还要接收另外两个参数,
    url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),      #^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$:表示以^articles/开头,第二位为0-9之间的四个任意数字组成,第三位为0-9之间任意两个数字组成,结尾为0-9之间的一个或多个数字组成
]

配置多个分组的URL时,在views.py文件定义函数时,URL每多一个分组函数里对应就要多一个形参来接收参数,示例如下:

1.urls.py中的url与函数对应关系配置
url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),              #^articles/([0-9]{4})/([0-9]{2})/$:表示以^articles/开头,中间为0-9之间任意数组成的四个数字,最后是0-9之间任意两个数字组成的URL结尾(三层(/a/0-9/0-9)可变的URL),这里要注意,由于URL分组了,URL对应的函数除了默认接收一个request参数外还要接收另外两个参数,

2.views.py中的函数写法
def month_archive(request,args1,args2):   #定义函数时需要有接收URL参数的形参
    print(args1)       #接收参数后可以操作参数
    print(args2)
    ···

注意事项:

  • urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续。
  • 若要从URL中捕获一个值,只需要在它周围放置一对圆括号(分组匹配)。
  • 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
  • 每个正则表达式前面的'r' 是可选的但是建议加上。
    补充说明

    # 是否开启URL访问地址后面不为/跳转至带有/的路径的配置项
    APPEND_SLASH=True

    Django settings.py配置文件中默认没有 APPEND_SLASH 这个参数,但 Django 默认这个参数为 APPEND_SLASH = True。 其作用就是自动在网址结尾加'/'。

其效果就是:
访问 http://www.example.com/blog 时,默认将网址自动转换为 http://www.example/com/blog/

当将参数设置为APPEND_SLASH=False时:
访问 http://www.example.com/blog 就会提示找不到页面,需要在http://www.example.com/blog/ 才可以实现访问。

2.2、分组命名匹配

上面的示例使用简单的正则表达式分组匹配(通过圆括号)来捕获URL中的值并以位置参数形式传递给视图。

在更高级的用法中,可以使用分组命名匹配的正则表达式组来捕获URL中的值并以关键字参数形式传递给视图。

在Python的正则表达式中,分组命名正则表达式组的语法是(?Ppattern),其中name是组的名称,pattern是要匹配的模式。

下面是以上URLconf 使用命名组的重写:

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]

这个实现与前面的示例完全相同,只有一个细微的差别:捕获的值作为关键字参数而不是位置参数传递给视图函数。
上面的URL参数对应的views.py里的函数写法如下:

def special_case_2003(request):
    print(request)

def year_archive(request,year):
    print(request,year)

def month_archive(request,year,month):
    print(request,year,month)

def article_detail(request,year,month,day)
    print(request,year,month,day)

在实际应用中,使用分组命名匹配的方式可以让你的URLconf 更加明晰且不容易产生参数顺序问题的错误,但是有些开发人员则认为分组命名组语法太丑陋、繁琐。

至于究竟应该使用哪一种,你可以根据自己的喜好来决定。
说明:
URLconf 在请求的URL 上查找,将它当做一个普通的Python 字符串。不包括GET和POST参数以及域名。

例如,http://www.example.com/myapp/ 请求中,URLconf 将查找myapp/。

在http://www.example.com/myapp/?page=3 请求中,URLconf 仍将查找myapp/。

URLconf 不检查请求的方法。换句话讲,所有的请求方法 —— 同一个URL的POST、GET、HEAD等等 —— 都将路由到相同的函数。
捕获的参数永远是字符串:
每个在URLconf中捕获的参数都作为一个普通的Python字符串传递给视图,无论正则表达式使用的是什么匹配方式,传递到视图函数中的参数永远是字符串类型的。

关于正则分组命名匹配和正则分组URL匹配的使用说明:
分组命名:通过给正则表达式命名的方式,函数接收的是关键字参数,实现调用
分组URL:没有给分组命令,函数接收的就是位置参数,实现调用
重点 : 两种类型的URL命名形式不可以混合使用,容易被覆盖。

2.3、试图函数中指定默认值

    # urls.py中
    from django.conf.urls import url
    from . import views

    urlpatterns = [      #两个不同url指定了一个函数
        url(r'^blog/$', views.page),          #默认返回Blog的第一页
        url(r'^blog/page(?P<num>[0-9]+)/$', views.page),         #返回Blog具体访问的页码数
    ]

    # views.py中,可以为num指定默认值
    def page(request, num="1"):
        pass

三、多级路由配置(一个django项目里有多个app)

1.创建两个app
创建app01和app02

在manage.py同级目录下执行以下命令创建app:
python manage.py startapp app01
python manage.py startapp app02

2.配置
在setting.py文件里的INSTALLED_APPS标签下:

INSTALLED_APPS = [
            'django.contrib.admin',
            'django.contrib.auth',
            'django.contrib.contenttypes',
            'django.contrib.sessions',
            'django.contrib.messages',
            'django.contrib.staticfiles',
            'app01.apps.App01Config',
            'app02.apps.App02Config',      #添加app01和app02的配置
        ]

3.在app01和app02下创建urls.py文件并添加如下默认配置
创建文件(PyCharm):New --> Python file --> 指定文件名字为:urls
文件中添加如下默认内容:

1.app01的urls.py配置如下:
    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views        #导入自己项目下的views文件

    #定义URL与函数的对应关系
    urlpatterns = [
        url(r'book',views.book)
    ]

2.app02的urls.py配置如下:
    from django.conf.urls import url
    from django.contrib import admin
    from app02 import views        #导入自己项目下的views文件

    #定义URL与函数的对应关系
    urlpatterns = [
        url(r'book',views.book)
    ]

试图(views.py)文件里的函数如下:

1.app01下的views.py文件的book函数内容如下:
from django.shortcuts import render,HttpResponse
def book(request):
    return HttpResponse("Welcome To App01")

2.app02下的views.py文件的book函数内容如下:
from django.shortcuts import render,HttpResponse

def book(request):
    return HttpResponse("Welcome To App02")

4.主urls.py文件(setting.py同级目录)里的配置如下

from django.conf.urls import url
from django.contrib import admin
from django.conf.urls import include        #导入include模块
from app01 import urls as app01_urls        #导入app01下的urls.py文件(URL与域名对应关系文件),为了防止和其他app冲突需要定义别名
from app02 import urls as app02_urls        #导入app02下的urls.py文件(URL与域名对应关系文件),为了防止和其他app冲突需要定义别名
urlpatterns = [
    url(r'^app01/', include(app01_urls)),       #如果是以app01开头的文件,就通过include导入app01的URL与函数的对应关系然后app01下的urls.py去处理
    url(r'^app02/', include(app02_urls)),       #如果是以app02开头的文件,就通过include导入app02的URL与函数的对应关系然后app01下的urls.py去处理
]

4.实现访问

通过:127.0.0.1:8000/app01/book           实现访问app01的项目
通过:127.0.0.1:8000/app02/book           实现访问app02的项目

原理说明:
用户 --> 请求:127.0.0.1:8000/app01/book --> 先到项目下的urls.py里找对应关系 --> 找到对应关系后到app下的urls.py里找对应关系 --> 最后到views.py里实现返回浏览器内容!!

四、传递额外的参数给试图函数(了解即可)

在配置URL与函数的对应关系时可以给额外传递一个或多个参数,示例如下:

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
]

在函数里就可以获取到传的参数
def year_archive(request,foo):
    print(foo)     #获取到这个参数后就可以操作这个参数
    ...

五、命名URL与反向解析

在使用Django 项目时,一个常见的需求是获得URL的最终形式,以用于嵌入到生成的内容中(视图中和显示给用户的URL等)或者用于处理服务器端的导航(重定向等)。

Django提供了一种机制,就是给我们的URL匹配规则起个名字,一个URL匹配模式起一个名字。

这样我们以后就不需要写死URL代码了,只需要通过名字来调用当前的URL即可。

5.1、有参数与没参数URL的命令

url(r'^home', views.home, name='home'),      # 给我的url匹配模式起名为 home
url(r'^book/(?P<year>[0-9]{2,4})/(?P<title>[a-zA-Z]{2})/$ ,views.book ,name="book"),       # 给url匹配模式起名为book(注意:这个url路径有关键字参数)
url(r'^book1/([0-9]{2,4})/([a-zA-Z]{2})/$ ,views.book ,name="book1"),       # 给url匹配模式起名为book1(注意:这个url路径有位置参数)

5.2、在模板文件中调用URL命名与试图文件中调用URL命令

在模板里面可以这样引用:
    <a href="{% url 'home' %}">点击跳转</a>       #没有参数的url反向解析
    <a href="{% url 'home' 2018 'dream' %}">点击跳转</a>       #有参数的url反向解析(模板语言里就按照位置写就可以了不用指定关键字)

在views函数中可以这样引用:
    from django.urls import reverse
    return redirect(reverse('home'))         #没有参数的url别名跳转
    return redirect(reverse('book', kwargs={"year":2019,"title":"dream"}))       #有关键字参数的URL别名跳转
    return redirect(reverse('book', args=("2018", "dream")))       #有位置参数的URL别名跳转

5.3、示例

1.在urls.py添加URL与函数解析设置

from django.conf.urls import url

urlpatterns = [
    url(r'^url_test1/', views.default_url_test,name="default_url"),      #没有参数的URL与函数映射
    url(r'^url_test2/([0-9]{2,4})$', views.parameter_url,name="parameter_url"),    #有参数的URL与函数映射
    url(r'^url_test3/(?P<path>[a-zA-Z]{2,4})$', views.keyworld_url,name="keyworld_url"),     #分组命名的URL与函数映射
]

2.在views.py中写对应的函数

from django.shortcuts import render,HttpResponse,redirect
from django.urls import reverse

def default_url_test(request):     #没有参数的URL对应的函数
    print("默认URL的路径:",reverse('default_url'))     #在views.py里通过reverse('default_url')获取要访问的路径,结果为:url_test1
    print("有参数的URL地址是:",reverse("parameter_url",args=("23",)))      #有参数的URL映射通过传位置参数的形式获取要访问的路径,结果为:url_test2/23
    print("关键字参数URL地址是:",reverse("keyworld_url",kwargs=({'path':'abc'})))      #分组命名的URL映射通过传关键字参数的形式获取要访问的路径,结果为:url_test3/abc
    return render(request,"url_test/default_url.html")

def parameter_url(request,args1):     #有位置参数的URL对应的函数
    return render(request,"url_test/parameter_url.html")

def keyworld_url(request,path):     #有关键字参数的URL对应的函数
    return render(request,"url_test/keyworld_url.html")

3.对应的HTML文件内容如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>URL测试</title>
</head>
<body>

<h1>无参数的URL命名测试文件</h1>

<p><a href="{% url 'parameter_url' '23' %}">跳转有参数</a></p>      <!-- html中通过:{% url 'parameter_url' '23' %}获取要访问的路径,前面是URl的命名,后面是参数 -->
<p><a href="{% url 'keyworld_url' 'abc' %}">跳转关键字参数</a></p>
{#<p><a href="{% url 'default_url' %}">跳转无参数</a></p>#}

</body>
</html>

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: