注意: 该篇章的代码都是在 urls.py 中编写的

路由的本质:
  • URL与要被该URL调用的视图函数之间的映射表
  • 通俗理解: 根据URL的不同,调用不同的视图函数(处理用户请求的函数),返回不同的html文件内容。即: 对于这个URL调用这段代码,对于那个URL调用那段代码

URL的配置


  • 基本格式 和 参数说明

    • 正则表达式: 一个正则表达式的字符串,用于匹配 URL 中的路径部分,且不包含 域名+端口 和 参数
    • views视图函数: 一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
    • 参数: 可选的要传递给视图函数的默认参数(字典类型
    • 别名: 一个可选的name参数,一般用于 url 的反向解析

from django.conf.urls import url
from django.shortcuts import HttpResponse, render, redirect
from app01.views

def 视图函数名称(request):
# 所有跟请求相关的数据,Django都帮你封装到 request 里面了
    return render(request, 'html路径')

def 视图函数名称(request):
    return render(request, 'html路径')

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

  • 基本用法

from django.conf.urls import url
from django.contrib import admin
from django.shortcuts import HttpResponse, render, redirect


def login(request):
    return render(request, 'login.html')


def str_fn(request):
    return HttpResponse('文字')


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', login),
    url(r'^str$', str_fn)
]

  • 在日常开发中我们会将所有的视图函数放到一个单独的py文件(即: views.py)中,然后通过相对引用或绝对引入将 views.py 文件导入到 urls.py 中,上面只是为了方便查看才上到 urls.py 中 (且一般我们都是以app的概念编写 views ,所以在开发中我们一般都是导入 app 中的 views.py,这只是为了突出写法二中的相对引入才把 views.py 从 app 中单离出来

    • 引入从 app 中单离出来的 views 

# urls.py 写法一

from django.conf.urls import url
from django.contrib import admin
from django.shortcuts import HttpResponse, render, redirect
from . import views  # 通过相对引入,将 views.py 文件导入到 urls.py 中

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.login),
    url(r'^str$', views.str_fn)
]

# urls.py 写法二

from django.conf.urls import url
from django.contrib import admin
from django.shortcuts import HttpResponse, render, redirect
from .views import *  # 通过相对引入,将 views.py 中的所有视图函数导入到 urls.py 中(通俗理解: 导入当前目录下的views中的所有视图函数,因为 . 代表着当前目录)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', login),
    url(r'^str$', str_fn)
]

# views.py

from django.shortcuts import HttpResponse, render, redirect


def login(request):
    return render(request, 'login.html')


def str_fn(request):
    return HttpResponse('文字')

    • 引入app中的 views.py

# urls.py 写法一

from django.conf.urls import url
from django.contrib import admin
from django.shortcuts import HttpResponse, render, redirect
from app01 import views  # 通过相对引入,将 views.py 文件导入到 urls.py 中

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.login),
    url(r'^str$', views.str_fn)
]

# urls.py 写法二

from django.conf.urls import url
from django.contrib import admin
from django.shortcuts import HttpResponse, render, redirect
from app01.views import *  # 通过相对引入,将 views.py 文件导入到 urls.py 中

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', login),
    url(r'^str$', str_fn)
]

# app01/views.py

from django.shortcuts import HttpResponse, render, redirect


def login(request):
    return render(request, 'login.html')


def str_fn(request):
    return HttpResponse('文字')

  • 注意: Django 2.0版本中的路由系统已经替换成下面的写法(官方文档):

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),
]

from django.urls import path
from django.contrib import admin
from django.shortcuts import HttpResponse, render, redirect


def login(request):
    return render(request, 'login.html')


def str_fn(request):
    return HttpResponse('文字')


urlpatterns = [
    path(r'^admin/', admin.site.urls),
    path(r'^login/', login),
    path(r'^str/', str_fn)
]

默认URL


  • 默认url的意思就是当匹配不到对应的url就使用默认的url
  • 默认url一般都会指定为首页或者404页面

from django.conf.urls import url
from django.contrib import admin
from app01.views import *  

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', login),
    url(r'^str$', str_fn),
    url(r'', index)  # 默认 url
]

# app01/views.py

from django.shortcuts import HttpResponse, render, redirect


def index(request):
    return render(request, 'index.html')


def login(request):
    return render(request, 'login.html')


def str_fn(request):
    return HttpResponse('文字')

URL正则表达式


from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^articles', views.special_case_2001),
    url(r'^articles2', views.special_case_2002),
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/([0-9]{4})/$', views.year_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]

  • 注意事项
    • urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续,例如:有一个 r'^articles' 和 r'^articles2' 的网址,当访问 r'^articles2' 的网址的时候它会访问 r'^articles' 的网址而不是 r'^articles2' 的网址,因为正则表达式匹配到 articles 就结束了
    • 若要从URL中捕获一个值,只需要在它周围放置一对圆括号(分组匹配)
    • 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles
    • 每个正则表达式前面的'r' 是可选的但是建议加上

  • 匹配结果的演示

    • 忽略 域名+端口 和 参数,因为只匹配 URL 中的路径部分

url(r'index/$', index)  # http://127.0.0.1:8001/index/?a=1 -> 忽略 域名+端口 和 参数 -> /index/ 匹配成功
                        # http://127.0.0.1:8001/index?a=1 -> 忽略 域名+端口 和 参数 -> /index 匹配失败

  • APPEND_SLASH 配置项 -> 了解

    • 是否在网址跳转前在网址结尾处加上 '/',
    • Django settings.py 配置文件中默认没有 APPEND_SLASH 这个参数,但 Django 默认这个参数为 APPEND_SLASH = True
    • 访问 http://www.example.com/blog 时,默认将网址自动转换为 http://www.example.com/blog/
    • 如果在settings.py中设置了APPEND_SLASH=False,此时我们再请求 http://www.example.com/blog 时就会提示找不到页面

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

urlpatterns = [
        url(r'^blog/$', views.blog),
]

# settings.py

APPEND_SLASH=True

URL的分组匹配和分组命名匹配


1. URL的分组匹配

  • 可以通过分组获取url中的参数然后传给视图函数
  • 分组匹配使用的是位置传参
  • 分组匹配,匹配到的都是字符串类型的参数
  • 注意: 如果使用了分组匹配,视图函数一定要接收这个参数,不然就会报错

# urls.py

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

urlpatterns = [
    url(r'^view_fn/(\d+)/', view_fn)
]

# views.py

from django.shortcuts import render, HttpResponse, redirect


def view_fn(request, num):
    print(num)
    return HttpResponse(num)

2. URL的分组命名匹配

  • 可分组匹配是一样的,只不过是对分组进行了命名
  • 分组命名匹配使用的是关键字传参
  • 分组命名匹配,匹配到的都是字符串类型的参数
  • 注意: 如果使用了分组命名匹配,视图函数一定要接收这个关键字参数,不然就会报错
  • 在日常开发中建议使用分组命名匹配

# urls.py

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

urlpatterns = [
    url(r'^view_fn/(?P<id>\d+)/', view_fn)
]

# views.py

from django.shortcuts import render, HttpResponse, redirect


def view_fn(request, id):
    print(id)
    return HttpResponse(id)

# 错误示范
# views.py

from django.shortcuts import render, HttpResponse, redirect


def view_fn(request, num):  # 接受的参数名一定要和分组的命名要一致
    print(num)
    return HttpResponse(num)

传递额外的参数给视图函数


  • 很少使用

# urls.py

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

urlpatterns = [
    url(r'^view_fn/', view_fn, kwargs={'name': 'Kevin', 'age': 18})
]

# views.py

def view_fn(request, name, age):
    print(name, age)
    return HttpResponse('{}, {}'.format(name, age))

视图函数默认值的使用


# urls.py

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

urlpatterns = [
    url(r'^view_fn/$', view_fn),
    url(r'^view_fn/page(?P<num>\d+)', view_fn)
]

# views.py

from django.shortcuts import render, HttpResponse, redirect


def view_fn(request, num=1):
    return HttpResponse('page:{}'.format(num))

include 分发


1.include

  • 在以前的写法中我们都是直接把所有的路径和app中的视图的函数映射关系写在 urls.py 一个文件中,如果映射关系过多那么这个文件的可读性就会很差,我们可以把和app自身有关的路径和视图函数的映射关系写在自身的app目录下,然后通过 include 将他导入进项目的 urls.py 中

  • 在以后日常开发中都要使用 include 

# 项目名/urls.py

from django.conf.urls import url, include
from app01 import urls as app01_urls
from app02 import urls as app02_urls

urlpatterns = [
    url(r'^teacher/', include(app01_urls)),  # 将以 teacher 开头的 url 分配给 app01 中的 urls.py
    url(r'^student/', include(app02_urls)),  # 将以 student 开头的 url 分配给 app02 中的 urls.py
]

# app01/urls.py

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

urlpatterns = [
    url(r'teacher_name/tid(?P<tid>\d+)', teacher)

# app01/views.py

from django.shortcuts import render, HttpResponse, redirect


def teacher(request, tid):
    return HttpResponse('tid:{}'.format(tid))

# app02/urls.py

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

urlpatterns = [
    url(r'student_name/sid(?P<sid>\d+)', student)
]

# app02/views.py

from django.shortcuts import render, HttpResponse, redirect


def student(request, sid):
    return HttpResponse('sid:{}'.format(sid))

  • 如果访问通过 include 配置的 url

http://127.0.0.1:8000/teacher/teacher_name/tid10
http://127.0.0.1:8000/student/student_name/sid10

命名URL(URL反向解析)


命名URL(URL反向解析)通俗理解: 通过url别名反向查找出实际的URL

命名URL(URL反向解析)的好处: 当我们修改了urls.py中的url路径也不会影响到视图函数或模板中使用了该路径的地方,因为是通过url别名反向查找出实际的URL

在以后日常开发中都要使用 命名URL(URL反向解析)

命名URL(URL反向解析)的使用方式:

reverse('url别名')
reverse('url别名', args=[位置参数])  # url使用了分组匹配
reverse('url别名', kwargs={关键字参数})  # url使用了分组命名匹配

reverse('命名空间名字:url别名')
reverse('命名空间名字:url别名', args=[位置参数])  # url使用了分组匹配
reverse('命名空间名字:url别名', kwargs={关键字参数})  # url使用了分组命名匹配

1. 不使用include的URL反向解析

  • 不带分组匹配的URL反向解析

# urls.py

from django.conf.urls import url, include
from app01.views import *

urlpatterns = [
    url(r'^book/(?P<bid>\d+)', book, name='book'),  # 给url起别名,用于url反向解析
    url(r'^home/', home, name='home')  # 给url起别名,用于url反向解析
]

# views.py

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


def book(request, bid):
    url = reverse('home')  # 使用reverse方法,通过url别名反向查找出实际的URL,需要导入 reverse 方法
    print(url)  # /home/
    return redirect(url)


def home(request):
    return HttpResponse('home')

# xxx.html 在模板中使用URL反向解析

<form action="{% url 'home' %}"></form>

  • 带有分组匹配的URL反向解析

# urls.py

from django.conf.urls import url, include
from app01.views import *

urlpatterns = [
    url(r'^book/(\d+)', book, name='book'),  # 给url起别名,用于url反向解析
    url(r'^home/', home, name='home')  # 给url起别名,用于url反向解析
]

# views.py

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


def book(request, bid):
    return HttpResponse('bid:{}'.format(bid))


def home(request):
    url = reverse('book', args=[10])  # 通过 args 参数使用带有分组匹配的URL反向解析
    print(url)  # /book/10
    return redirect(url)

# xxx.html 在模板中使用URL反向解析

<form action="{% url 'book' 10 %}"></form>

  • 带有分组命名匹配的URL反向解析

# urls.py

from django.conf.urls import url, include
from app01.views import *

urlpatterns = [
    url(r'^book/(?P<bid>\d+)', book, name='book'),  # 给url起别名,用于url反向解析
    url(r'^home/', home, name='home')  # 给url起别名,用于url反向解析
]

# views.py

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


def book(request, bid):
    return HttpResponse('bid:{}'.format(bid))


def home(request):
    url = reverse('book', kwargs={'bid': 20})  # 通过 kwargs 参数使用带有分组命名匹配的URL反向解析
    print(url)  # /book/20
    return redirect(url)

# xxx.html 在模板中使用URL反向解析

<form action="{% url 'book' bid=10 %}"></form>

2. 使用include的URL反向解析

  • 带有分组匹配和分组命名匹配的URL反向解析的使用方式,在这里就不做演示了和上面的用法是一样的

  • 使用include的URL反向解析

# 项目名/urls.py

from django.conf.urls import url, include
from app01 import urls as app01_urls
from app02 import urls as app02_urls

urlpatterns = [
    url(r'^teacher/', include(app01_urls)),
    url(r'^student/', include(app02_urls)),
]

# app01/urls.py

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

urlpatterns = [
    url(r'teacher_name/tid(?P<tid>\d+)', teacher, name='teacher_name')  # 给url起别名,用于url反向解析
]

# app01/views.py

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


def teacher(request, tid):
    url = reverse('student_name', kwargs={'sid': 10})  # 通过 kwargs 参数使用带有分组命名匹配的URL反向解析
    print(url)  # /student/student_name/sid10
    return redirect(url)

# app02/urls.py

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

urlpatterns = [
    url(r'student_name/sid(?P<sid>\d+)', student, name='student_name')  # 给url起别名,用于url反向解析
]

# app02/views.py

from django.shortcuts import render, HttpResponse, redirect


def student(request, sid):
    return HttpResponse('sid:{}'.format(sid))

# xxx.html 在模板中使用URL反向解析

<form action="{% url 'teacher_name' tid=10 %}"></form>
<form action="{% url 'student_name' sid=10 %}"></form>

3. 命名空间

  • 当两个url别名相同的时候我们可以通过命名空间来进行区分,前提是要在使用include的URL反向解析下使用

  • 使用方式: reverse('命名空间的名字:url别名')

# 项目名/urls.py

from django.conf.urls import url, include
from app01 import urls as app01_urls
from app02 import urls as app02_urls

urlpatterns = [
    url(r'^teacher/', include(app01_urls, namespace='teacher')),  # 对 include 所引入的 urls.py 起别名(即:命名空间的名字)
    url(r'^student/', include(app02_urls, namespace='student')),  # 对 include 所引入的 urls.py 起别名(即:命名空间的名字)
]

# app01/urls.py

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

urlpatterns = [
    url(r'teacher_name/tid(?P<tid>\d+)', teacher, name='app_name')  # 给url起别名,用于url反向解析
]

# app01/views.py

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


def teacher(request, tid):
    url = reverse('student:app_name', kwargs={'sid': 10})  # 通过 reverse('命名空间的名字:url别名') 解决 url别名 重复的问题
    print(url)  # /student/student_name/sid10
    return redirect(url)

# app02/urls.py

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

urlpatterns = [
    url(r'student_name/sid(?P<sid>\d+)', student, name='app_name')  # 给url起别名,用于url反向解析
]

# app02/views.py

from django.shortcuts import render, HttpResponse, redirect


def student(request, sid):
    return HttpResponse('sid:{}'.format(sid))

# xxx.html 在模板中使用URL反向解析

<form action="{% url 'teacher:app_name' tid=10 %}"></form>
<form action="{% url 'student:app_name' sid=10 %}"></form>

4. 在模板中对url使用反向解析

# url使用了分组匹配,那么就使用位置参数
# url使用了分组命名匹配,那么就使用关键字参数

# 没有使用 include 
{% url 'url名称' %}
{% url 'url名称' 位置参数1 位置参数2 %}
{% url 'url名称' 关键字参数1 关键字参数2 %}

# 使用了 include
{% url '命名空间的名字:url名称' %}
{% url '命名空间的名字:url名称' 位置参数1 位置参数2 %}
{% url '命名空间的名字:url名称' 关键字参数1 关键字参数2 %}

# 没有使用 include
{% url 'index' %}
{% url 'show_news' 1 2 %}
{% url 'show_news2' category=1 pageNo=2 %}

# 使用了 include
{% url 'app01:index' %}
{% url 'app01:show_news' 1 2 %}
{% url 'app01:show_news2' category=1 pageNo=2 %}

url 层级分发


  • url 层级分发 和 include 分发有点类似 

  • 第一个None表示的是 命名url中的 name,第二个None表示的是 App 的名字

# 固定格式

url(r'路径', ([

    url(r'^路径', ([
url……
    ], None, None)),

    url(r'路径', 视图函数),

], None, None))

# urls.py

urlpatterns = [

    url(r'views/', ([

        url(r'^views_1/', ([
            url(r'^views_1_1/', views.views_1_1),
            url(r'^views_1_2/', views.views_1_2),
            url(r'^views_1_3/', views.views_1_3),
        ], None, None)),
        url(r'^views_2/', ([
            url(r'^views_2_1/', views.views_2_1),
            url(r'^views_2_2/', views.views_2_2),
        ], None, None)),
        url(r'views_3/', views.views_3),
    ], None, None))

]

'''
    最终生成的url

    http://127.0.0.1:8000/views/views_1/views_1_1/
    http://127.0.0.1:8000/views/views_1/views_1_2/
    http://127.0.0.1:8000/views/views_1/views_1_3/
    http://127.0.0.1:8000/views/views_2/views_2_1/
    http://127.0.0.1:8000/views/views_2/views_2_2/
    http://127.0.0.1:8000/views/views_3/
'''