对照这django官方教程(1.8)写第一个APP,在第3部分(Removing hardcoded URLs in templates),将index.html的链接<a href="/polls/{{ question.id }}/">
更改为<a href="{% url 'polls:detail' question.id %}">
,期望输出polls/1
之类的网址。
运行测试网站http://127.0.0.1:8000/polls/
时却发生错误:
NoReverseMatch at /polls/
Reverse for 'detail' with arguments '(2,)' and keyword arguments '{}' not found. 1 pattern(s) tried:[u'$(?P<pk>[0-9]+)/$']
百思不得其解,为什么url解析错误。
当时的polls/urls.py:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),
url(r'^(?P<pk>[0-9]+)/results/$', views.ResultsView.as_view(), name='results'),
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]
polls/template/polls/index.html:
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
查看url tag的文档,里面是这么描述url tag的功能。
url
Returns an absolute path reference (a URL without the domain name) matching a given view and optional parameters.For example, suppose you have a view, app_views.client, whose URLconf takes a client ID (here, client() is a method inside the views file app_views.py). The URLconf line might look like this:
('^client/([0-9]+)/$', app_views.client, name='app-views-client')
If this app’s URLconf is included into the project’s URLconf under a path such as this:
('^clients/', include('project_name.app_name.urls'))
...then, in a template, you can create a link to this view like this:
{% url 'app-views-client' client.id %}
The template tag will output the string /clients/client/123/.
Note that if the URL you’re reversing doesn’t exist, you’ll get an NoReverseMatch exception raised, which will cause your site to display an error page.
说的是不存在想要反析的URL就产生NoReverseMatch异常,反析的过程应该就是让网址括号里的正则表达式里匹配传过去的参数(参考Reverse resolution of URLs)。
在之前的index.html,question.id作为参数传过去,结果不匹配。竟怀疑question.id不为数字,直接将参数改为10,结果仍然是产生了NoReverseMatch异常:
NoReverseMatch at /polls/
Reverse for 'detail' with arguments '(10,)' and keyword arguments '{}' not found. 1 pattern(s) tried:[u'$(?P<pk>[0-9]+)/$']
但注意到参数发生了变化,确实变成了10, arguments '(**10**,)'
。既然传输的参数是正确的,那问题还是出在正则表达式的网址上。
仔细查看错误提示,试验了1个正则表达式(1 pattern(s) tried: [u'$(?P<pk>[0-9]+)/$']
),这个试验的正则表达式有点怪异,前面居然有一个匹配结尾元字符$
,而polls/urls.py中name='detail'对应的网址正则表达式为r'^(?P<pk>[0-9]+)/$'
。
逐想到上层mysite/urls.py中的配置,查看mysite/urls.py:
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^$', include('polls.urls', namespace="polls")),
url(r'^polls/', include('polls.urls', namespace="polls")),
url(r'^admin/', include(admin.site.urls)),
]
居然定义了两个namespace="polls",当执行{% url 'polls:detail' question.id %}
时,django先找到mysite/polls第一个namespace=polls的url regex,然后加上polls/urls.py对应name=detail的url regex,得到最后的url regex: $(?P<pk>[0-9]+)/$
,这个正则式要求第一个位置匹配结束,这估计怎么都不会反析成功。而正确的url regex应该是第二个namespace=polls的url regex加上polls/urls.py对应name=detail的url regex,既polls/(?P<pk>[0-9]+)/$
。
发现问题所在后,将第一个namespace=polls所在行删掉,再次打开http://127.0.0.1:8000/polls/
,正确的网页显示出来了,而且每一个问题对应的网址就是http://127.0.0.1:8000/polls/[数字]
。
django先应用找到的第一个namespace,所以将第一个namespace和第二个互换位置,结果也是可以正常显示。
惨痛的教训告诫我们,不要在网站的urls.py使用同一个名字的namespace。另外,网站的urls.py若使用了include,不要在正则式后再加$
。参考官方文档:Including other URLconfs
https://www.cnblogs.com/kekeyu/p/4948555.html