注意: 该篇章的代码都是在 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('文字')
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/
'''