小谈Django中的路由系统
Django的路由系统
URLconf配置
from django.conf.urls import url
urlptterns = [
url(正则表达式,views视图,参数,别名),
]
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^publisher/', views.publisher),
url(r'^add_publisher/', views.add_publisher),
url(r'^del_publisher/', views.del_publisher),
url(r'^edit_publisher/', views.edit_publisher),
url(r'^book/', views.book),
url(r'^add_book/', views.add_book),
url(r'^del_book/', views.del_book),
url(r'^edit_book/', views.edit_book),
url(r'^author/', views.author),
url(r'^add_author/', views.add_author),
url(r'^del_author/', views.del_author),
]
settings.py中的ROOT_URLCONF
ROOT_URLCONF = 'bookmanager.urls'
# 上面这个指定的就是urls.py中的urlpatterns
示例一
urlpatterns = [
url(r'^admin/', admin.site.urls),
# ^ 以什么开头 ; $ 以什么结尾
url(r'^blog/$', views.blog),
# [0-9]{4} 4位0-9之间的数字(可以代表年份) ; \d{2} 2位数字(可以代表月份)
# 想要访问此页面,网址:http://127.0.0.1:8000/blog/2019/12/
url(r'^blog/[0-9]{4}/\d{2}', views.blogs),
]
# urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^blog/$', views.blog),
# 继上面的操作后又加入了分组
# 分组后网页无法显示,显示函数缺少两个参数
# 所以要在views.py中的blogs加入两个参数
url(r'^blog/([0-9]{4})/(\d{2})/$', views.blogs),
]
# views.py
def blog(request):
return HttpResponse('blog')
# request后面的两个参数是必须要加的,因为urls中的路径分组了
def blogs(request,year,month):
print(year,type(year))
print(month)
return HttpResponse('blogs')
# 登录后print出来的结果:
2019 <class 'str'>
12
# 这两个值可以直接拿到,也可以直接用在函数中.下面以del_publisher为例
示例二
# urls页面
urlpatterns = [
url(r'^admin/', admin.site.urls),
...
url(r'^del_publisher/(\d+)', views.del_publisher),
...
# views页面
def del_publisher(request,pk):
# pk = request.GET.get('pk')
obj = models.Publisher.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('您所查询的出版社不存在!')
obj.delete()
return redirect('/publisher/')
# publisher.html
{% for publisher in all_publishers %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ publisher.id }}</td>
<td>{{ publisher.name }}</td>
{# <td><a href="/del_publisher/?pk={{ publisher.pk }}">删除</a></td>#}
<td><a href="/del_publisher/{{ publisher.pk }}/">删除</a></td>
<td><a href="/edit_publisher/?pk={{ publisher.pk }}">编辑</a></td>
</tr>
{% endfor %}
分组后:
url(r'^del_publisher/(\d+)', views.del_publisher),
# 分组后从url中捕获参数,捕获的参数会按照 位置传参 传递给函数
命名分组
示例一:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^blog/$', views.blog),
url(r'^blog/(?P<year>[0-9]{4})/(?P<month>\d{2})/$', views.blogs),
]
# 分组后从url中捕获参数,捕获的参数会按照 关键字传参 传递给函数
def blogs(request,month,year):
print(year,type(year))
print(month)
return HttpResponse('blogs')
# print结果:
2019 <class 'str'>
99
# 关键字传参:传参时位置变化没影响,但是参数名字要一致
注意事项:
- urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续。
- 若要从URL中捕获一个值,只需要在它周围放置一对圆括号(分组匹配)。
- 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
- 每个正则表达式前面的'r' 是可选的但是建议加上。
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字符串传递给视图,无论正则表达式使用的是什么匹配方式。例如,下面这行URLconf 中:
url(r'^blog/(?P<year>[0-9]{4})/(?P<month>\d{2})/$', views.blogs)
传递到视图函数views.year_archive()
中的year
month
参数永远是一个字符串类型
视图函数中指定默认值
比如:路由中两个路径指向同一个函数时,一个不需要传参,一个需要传参,这个时候可以给参数指定默认值,如下:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^blog/$', views.blog),
url(r'^blog/(?P<year>[0-9]{4})/(?P<month>\d{2})/$', views.blog),
]
def blog(request,*args,**kwargs):
return HttpResponse('blog')
include其他的URLconfs
比如有多个app时,所有的路由还都写在urls.py中,耦合性太高,这个时候需要把路由分发,给路由解耦
做法:把urls.py复制到app01中,然后把和settings.py同级的urls中除了admin,把其他都删掉(或者注释),再利用include把路由再一次分发
# 和settings.py同级的urls
from django.conf.urls import url,include
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# url(r'^blog/$', views.blog),
# url(r'^blog/(?P<year>[0-9]{4})/(?P<month>\d{2})/$', views.blog),
url(r'^', include('app01.urls')),
]
# app01中的urls
from django.conf.urls import url
# from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^blog/$', views.blog),
url(r'^blog/(?P<year>[0-9]{4})/(?P<month>\d{2})/$', views.blog),
]
# 如果第一个urls中的include前面^后面加上app01 ==> 启动项目后每个路径都要加上app01这个前缀
# 至于为什么^后面要加上app01 ==> 因为有可能有多个app
# 为什么要用include,因为以后项目不是一个人开发时(需要协同开发时),有可能每个人开发一个app,这样利于管理
url的命名和反向解析
静态的
命名
app01的urls
url(r'^login/', views.login, name='login'),
反向解析
模板中:
{% url 'login' %}
# 1.如果settings.py同级的urls中是这样:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^', include('app01.urls')),
{% url 'login' %} ==> 反向解析成 "/login/"
# 2.如果settings.py同级的urls中是这样:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^app01/', include('app01.urls')),
{% url 'login' %} ==> 反向解析成 "/app01/login/"
py文件中:
from django.urls import reverse
reverse('login') ==> "/app01/login/"
示例:
app01的urls
url(r'^classes/', views.classes, name='class'),
views中
from django.shortcuts import render, redirect, HttpResponse,reverse
add_classes函数中重定向到classes.html页面的代码:
# return redirect('/classes/')
url = reverse('class')
return redirect(url)
分组的
(命名分组都一样,就是有没有?P<year>
和?P<month>
, 这个还意味着传参方式的区别,命名分组是关键字传参)
命名
# app01中的urls
urlpatterns = [
url(r'^blog/$', views.blog),
url(r'^blog/(?P<year>[0-9]{4})/(?P<month>\d{2})/$', views.blog, name='blog'),
]
反向解析
模板中
<a href="{% url 'blog' 2019 10 %}">2019-10</a> ==> /blog/2019/10/
# 要提供相应的参数
# 下面是命名分组的关键字传参
<a href="{% url 'blog' month=10 year=2019 %}">2019-10</a>
py文件中
def blog(request,*args,**kwargs):
print(reverse('blog',args=(2019,10)))
/blog/2019/10/
# 命名分组的关键字传参方式
print(reverse('blog',kwargs={'year':2019,'month':10}))