Django中URLconf的基本用法
在DJANGO中,WEB页面和其它内容都由视图产生。每一个视图都是由一个简单的PYTHON函数(或方法,在基于类的视图中)描述的。DJANGO将通过测试请求(REQUEST)的URL选择一个视图(更精准的来说是域名后的URL部分)。
URL模式很简单,一般模式是,如:/newsarchive/<year>/<month>/.
从URL到一个视图,DJANGO使用’URLconfs’。URLconf包含了URL模式到视图的映射。
写更多的视图
现在让我们在polls/views.py中添加更多的视图。这些视图有点不同,因为它们带有参数:
polls/views.py def detail(request, question_id): return HttpResponse("You're looking at question %s." % question_id) def results(request, question_id): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id)
通过path()
连接这些试图到polls.urls模块中:
polls/urls.py from django.urls import path from . import views urlpatterns = [ # ex: /polls/ path('', views.index, name='index'), # ex: /polls/5/ path('/', views.detail, name='detail'), # ex: /polls/5/results/ path('/results/', views.results, name='results'), # ex: /polls/5/vote/ path('/vote/', views.vote, name='vote'), ]
在浏览器中输入”/polls/34/”,它会执行detail()方法,并显示你在URL中提供的ID。而”/polls/34/results/”和”/polls/34/vote/”会分别调用results和vote页面。
当有人从web站点请求一个页面时,Django会加载mysite.urls模块,因为它被ROOT_URLCONF
设置。它会找到名为urlpatterns
的变量,并按顺序通过(traverses)模式。在匹配到”polls/”后,它去除掉匹配的字符(“polls”),并把剩余的字符——”34/”发送到”polls.urls”URLconf模块中,用于之后的处理。这里会匹配”<int:question_id>/
“,结果是调用detail()视图,调用过程如下:
detail(request=<HttpRequest object>, question_id=34)
question_id=34
部分来自”<int:question_id>/”。使用尖括号“捕获”的URL部分,并发送它做为关键字参数到视图函数。“question_id>”部分字符串定义的名字将于识别被匹配的模式。“<int:”部分是一个转换器,它决定了什么样的模式将匹配这段URL路径。
这里不需要添加URL扩展名,就像.html——除非你非要这么做,在这种情况下你能这样做:
path('polls/latest.html', views.index),
但是这么做很傻。
写一个有实际作用的视图
每一个视图可以做两种事情中的一种:(1)返回一个HttpResponse
对象,包含了请求页的内容。(2)引发一个异常,就像Http404
,剩下的取决于你怎么做。
你的视图能从数据库读记录,能使用模板系统,能产生PDF文件,能输出XML,能创建ZIP文件,任何你想做的事,使用你想用的PYTHON库。
在系统中显示最近5个poll请求,根据发布日期用逗号分开:
polls/views.py from django.http import HttpResponse from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] output = ', '.join([q.question_text for q in latest_question_list]) return HttpResponse(output)
这有一个问题,思考一下:页面的设计在视图中是硬编码的。如果你想改变视图的外观,就必须编辑PYTHON代码。所以,让我们使用Django的模板系统,为视图创建一个模板,把设计从PYTHON分离出来。
首先,在polls
目录里创建一个名为templates
目录。Django将会在这里查找模板。
你项目的TEMPLATES
描述了DJANGO将如何加载和提供模板。默认的设置配置了一个DjangoTemplates
后台,它的APP_DIRS
选项设置成True。习惯上,DjangoTemplates
在每一个INSTALLED_APPS
中查找“templates”子目录。
在你刚建立的templates
目录里,创建别一个名为polls的目录,并且创建一个名为index.html的文件。换句话说,你的模板应该在polls/templates/polls/index.html
。因为app_directories
模板的加载工作和上面描述的一样,可以简单的进行一个软连接到polls/index.html。
模板的名字空间
现在我们可以放下模板,直接离开polls/templates目录了,但是这实际上是个坏主意。Django会选择第一个它找到名字匹配的模板,并且如果你有一个模板在不同的应用使用同样的名字,Django将不能区分它们。我们需要为Django指出正确的那一个,并且最简单的方法是使用名字空间来区分他们。因此,要把这些模板放到自己应用目录中应用名的目录中(That is, by putting those templates inside another directory named for the application itself.)。
如下代码放入模板中:
polls/templates/polls/index.html {% if latest_question_list %} <ul> {% for question in latest_question_list %} <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li> {% endfor %} </ul> {% else %} <p>No polls are available.</p> {% endif %}
现在让我们在polls/views.py中更新一下index视图,让其使用模板:
polls/views.py from django.http import HttpResponse from django.template import loader from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] template = loader.get_template('polls/index.html') context = {'latest_question_list': latest_question_list, } return HttpResponse(template.render(context, request))
这段代码加载了名为polls/index.html
的模板,并且发送了它的“内容”。这个“内容”是映射了模板变量名到PYTHON对象的目录。
在浏览器中输入“/polls/”加载页面,会看到一个项目符号列表,其中包含了我们的“问题”。这些问题连接到问题 的详情(detail视图)页面。
快捷方式:render()
这是用来加载模板的常用用法,填入内容并返回一个带有模板结果的HttpResponse对象。Django提供一个快捷方式。重写index()视图:
polls/views.py from django.shortcuts import render from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] context = {'latest_question_list': latest_question_list} return render(request, 'polls/index.html', context)
注意:一旦我们在所有视图中使用了这种快捷方式,我们就不用再导入loader
和HttpResponse
(如果你仍然有原始的方法,如:detail,results,vote。那么你还是需要导入HttpResponse
的)。
render()函数用request对象做为它的第一个参数,模板名做为它的第二个参数,第三个参数是一个可选填的字典(dict)类型(需要添加到模板中的内容context)。它会根据所选择的模板填写上给予的内容(context)后返回一个HttpResponse
对象。
发出404错误
现在让我们来处理一下detail视图:
polls/views.py from django.http import Http404 from django.shortcuts import render from .models import Question # ... def detail(request, question_id): try: question = Question.objects.get(pk=question_id) except Question.DoesNotExist: raise Http404("Question does not exist") return render(request, 'polls/detail.html', {'question': question})
一个新的概念:如果请求的页面不存在,视图发出一个Http404
异常。
现在想让上面的例子运行,需要一个简单的模板来支撑,内容如下:
polls/templates/polls/detail.html {{ question }}
现在启动服务器就能看到运行状态。
快捷方式:get_object_or_404()
如果对象不存在,常使用get()和Http404()这两个函数。Django提供了一个快捷方式。重写detail()视图来看一下:
polls/views.py from django.shortcuts import get_object_or_404, render from .models import Question # ... def detail(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/detail.html', {'question': question})
get_object_or_404()
函数的第一个参数是一个Django模型,后面是任意个关键字参数,它把这些参数传递给get()
函数,如果对象不存在会引发Http404
异常。
同样还有一个get_list_or_404()
函数,它的工作方式和get_object_or_404()
一样——除了使用filter()
代替get()
。如果列表为空,会引发Http404
。
使用模板系统
编辑一下polls/detail.html模板:
<h1>{{question.question_text}}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }}</li> <% endfor %} </ul>
模板系统使用“点查找”语法访问变量属性。在本例中,{{ question.question_text }},首先Django先对question对象进行字典查找。如果不行,它尝试进行属性查找——在本例中属性查找成功了。如果属性查找失败了,它将会尝试列表索引查找。
方法调用发生在{% for %}循环中:question.choice_set.all的功能是返回Choice对象的迭代器,之后用于{% for %}段。
在模板中移除硬链接
记注:当我们在polls/index.html模板中给question一个链接时,硬链接的部分代码如下:
<li><a href="/polls/{{ question.id }}/"> {{ question.question_text }} </a></li>
这个硬链接有一个问题,在项目中大量的模板里使用这种紧密耦合的方法不利于改变URL。不过,在polls.urls模块中path()函数的名字参数(最后一个参数)可以移除特定URL的依赖性,通过在模板中使用模板标记{% url %}调用URL名:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
这种方法的工作方式是通过查找在polls.urls模块内定义的URL名。名为“detail”的URL定义如下:
... # 'name'的值被{% url %}模块标签调用 path('<int:question_id/', views.detail, name='detail'), ...
如果你想改变polls的detail视图的URL(比如改成:polls/specifics/12/),可以在polls/urls.py中这样修改:
#添加关键字'specifics' path('specifics/<int:question_id>/', views.detail, name='detail'), ...
URL名字空间
在我们的例子中只有一个应用——polls,但是在真实的项目中可能有很多应用,这时不同的应用中都有命名为detail的URL怎么办?方法是在URLconf中添加名字空间。
打开polls/urls.py文件,在开头添加app_name
用于设置名字空间:
from django.urls import path from . import views app_name = 'polls' urlpatterns=[path('',views.index, name='index'), ...]
现在改变模板polls/index.html中的调用名:
<li>< href="{% url 'polls:detail' question.id %}"> {{ question.question_text}}</a></li>