Django中URLconf的基本用法

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)

注意:一旦我们在所有视图中使用了这种快捷方式,我们就不用再导入loaderHttpResponse(如果你仍然有原始的方法,如: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>

发表评论