Django中的视图的概念是:一类具有相同功能和模板的网页的集合。
在Django中,网页和其他内容都是从视图派生而来。每一个视图表现为一个简单的Python函数(或是方法,如果是在基于类的视图里的话)。Django将会根据用户请求的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)
然后在polls.urls中添加url
# 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/",Django将会运行detail()方法并且展示在URL提供的问题ID。
当某人请求网站的某一页面时,例"polls/34/",Django将会载入mysite.urls模块,因为这个配置项ROOT_URLCONF中设置了。
然后Django寻找名为urlpatterns变量并且按序匹配正则表达式。找到匹配项"polls/",它切掉了匹配的文本(“polls/”),将剩余文本"34/",发送至"polls.urls",URLconf做进一步处理。
在剩余文本匹配了"int:question_id/",使得Django以如下形式调用detail()
detail(request=<HttpRequest object>, question_id=34)
question_id=34由int:question_id匹配生成。使用尖括号”捕获"这部分URL,且以关键字参数的形式发送给视图函数。
上述字符串的:question_id部分定义了将被用于区分匹配模式的变量名,而int:则是一个转换器决定了应该以什么变量类型匹配这部分的URL路径。
每个视图必须要做的只有两件事
视图可以从数据库里读取记录,可以使用一个模板引擎,可以生成一个PDF文件,或者其他事情。
Django只要求返回的是一个HttpResponse,或者抛出一个异常。
在index()函数里插入一些内容,让它能展示数据库里以发布日期排序的最近5个投票问题
# 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)
# Leave the rest of the views (detail, results, vote) unchanged
页面的设计写死在视图函数的代码里面。可以通过Django的模板系统创建一个视图,就可以将页面的设计从代码中分离出来。
在polls目录里创建一个templates目录,Django将会在这个目录里查找模板文件。
项目的TEMPLATES的配置描述了Django如何载入和渲染模板,默认的设置文件设置了DJangoTemplates后端,并将APP_DIRS设置成了True。这一选项将会让DjangoTemplates在每个INSTALLED_APPS文件夹中寻找"templates"子目录。
在templates目录里,创建一个目录polls,然后再其中新键一个index.html。此时的模板文件的路径是polls/templates/polls/index.html。因为Django会寻找到对应的app_directories,所以只需要使用polls/index.html就可以引用这一模板
虽然可以将模板文件直接放在polls/templates文件加中(而不是再创建一个polls子文件夹),因为Django会选择第一个匹配的模板文件,如果由一个模板文件和另一个应用中的某个模板文件重名,Django没有办法区分它们。
所以最简单的办法就i是把它们放入各自的命名空间中,也就是把这些模板放入一个和自身应用重名的子文件夹里。
新建html
# 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
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模板文件,并且向它传递一个上下文(context)。这个上下文是一个字典,它将模板内的变量映射为Python对象。
载入模块,填充上下文,再返回由它生成的HttpResponse对象是一个非常常用的操作流程。
Django提供了一个快捷函数
# 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)
处理投票详情视图
# 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})
如果指定问题ID所对应的问题不存在,这个视图就会抛出一个Http404异常。
尝试用get()函数获取一个对象,如果不存在就抛出Http404错误也是一个普遍流程。
Django也提供了一个快捷函数
# 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_list_or_404()函数,工作原理和get_object_or_404()一样。如果列表为空则抛出Htto404异常。
查看detail()视图,它向模板传递了上下文变量qeustion
# polls/templates/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对象使用字典查找(也就是使用obj.get(str)操作),如果失败了就尝试属性查找(也就是obj.str操作),如果这一操作也失败了,将会尝试列表查找(也就是obj[int]操作)
question.chaoice_set.all被解释为Python代码question.choice_set.all()
,将会返回一个可迭代的Choice对象,这一对象可以在{% for %}标签内部使用。
polls/index.html里编写投票链接时,链接是硬编码
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
硬编码对于包含很多应用的项目来说,修改起来是十分麻烦的。解决办法是在polls.urls里的url()函数中通过name参数为URL定义名字。然后使用{% url %}标签代替它。
...
# the 'name' value as called by the {% url %} template tag
path('/' , views.detail, name='detail'),
...
在模板中的使用
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
如果想改变投票详情视图的URL,例polls/specifics/12/,就不需要再模板里修改任何东西,只要再urls.py里修改就可以
...
# added the word 'specifics'
path('specifics//' , views.detail, name='detail'),
...
目前只有一个应用,polls。如果应用增加到10个、20个或者更多。Django可以通过添加命名空间来防止无法分辨重名的URL。
# polls/urls.py
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.index, name='index'),
path('/' , views.detail, name='detail'),
path('/results/' , views.results, name='results'),
path('/vote/' , views.vote, name='vote'),
]
原polls/index.html
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
重新编辑的polls/index.html
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>