• 第三节:视图(Views)和模板(Templates)


    目录

    概览

    视图是Django应用的网页的“类型”,一般服务于特定的功能并且有特定的模板,在我们的投票应用中,我们有下面四个视图:

    • Question  "index"页面——展示最新的几个问题。
    • Question  "detail"页面——显示问题内容,没有结果但是有一个投票表单。
    • Question  "results"页面——显示一个特定问题的结果
    • Vote "action"——处理在一个特定问题的进行具体的选择

    在Django中,Web页面和其他内容是通过一个视图提供的,每一个视图由一个简单的Python函数表示(或者是类方法),Django将解析被请求的Url去选择一个视图(准确地说,是url域名后面的部分)。

    从一个url到一个视图,Django使用所谓的‘Urlconfs’一个Urlconf映射Url模式到一个视图,本教程提供了Urlconfs使用的基本介绍,你也可以参考django.urls获得更多信息。 

    编写视图

    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映射:

    from django.conf.urls import url
    
    from . import views
    
    urlpatterns = [
        # 例: /polls/
        url(r'^$', views.index, name='index'),
        # 例: /polls/5/
        url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
        # 例: /polls/5/results/
        url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
        # 例: /polls/5/vote/
        url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
    ]

    在浏览器打开“/polls/34”它将调用detail()方法并且显示你在url中提供的ID,同样的可以尝试打开“/polls/34/results”和“/polls/34/vote”。

    当有人请求你网站的一个页面时,比如“/polls/34/”,Django将加载 mysite.urls Python模块因为它指定在ROOT_URLCONF配置中(mysite/settings.py)。它查找名为urlpatterns的变量并且按顺序遍历正则表达式。然后发现匹配"^polls/",它会替换掉匹配的文本("polls/")并将剩余的文本-"34/"交给"polls.urls"的url配置做进一步处理。它匹配r'^(?P<question_id>[0-9]+)/$',结果调用detail()视图:

    detail(request=<HttpRequest object>, question_id='34')

    这个question_id='34'部分来自(?P<question_id>[0-9]+)。使用括号通过这个模式在一个“捕获”匹配的文本,并发送它给视图函数作为一个参数。?P<question_id>将被用做匹配模式的标识名称;[0-9]+匹配数字序列的正则表达式。 

    因为Url模式是正则表达式,所以格式没有任何限制,比如我们设置一个.html后缀的地址:

    url(r'^polls/latest.html$', views.index),

    但是没必要这样做。

    编辑视图实际做一些事情

    每个视图负责两个事情之一,返回HttpResponse对象包含请求页面的内容,或者抛出异常例如Http404 

    你的视图可能从数据库读取记录,它可以使用一个模板系统例如Django的,-或者第三方Python模板系统。它可以产生一个PDF文件,输出XML,创建一个ZIP文件,任何你想要的,都可以使用Python库。 

    因为它的方便,让我们使用Django自己的数据库API,在第二部分教程中已经提到过了,在index()视图,我们显示最新的五条投票问题,以逗号分隔,按发布时间排序(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

    注意:现在我们可能把我们的模板文件直接放在polls/templates(而不是创建另外一个polls子目录),但它实际上不是一个好主意。Django将选择第一个名称相匹配的模板,并且如果你已经有一个在不同应用名称相同的模板,Django是无法区分他们的,我们需要一个正确的指向项目模板并确保他们有简单的命名方式,也就是说通过将模板放置到templates的另一个目录中。

    在模板中输入以下代码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视图去使用模板: 

    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变量,context是一个字典映射在模板的变量名。 

    通过在你的浏览器加载页面“/polls/”,然后你应该可以看到Question的一个列表。 

    一个快捷方法:render()

    它是一个加载模板非常常见的语法,添加一个context并返回呈现模板结果的

    HttpResponse对象。Django提供一个快捷方法,现在我们来重写index()视图:

    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 

    这个render()函数以请求对象作为第一个参数,第二个参数是模板名称和另外一个参数字典类作为可选参数。它返回一个指定上下文指定模板的HttpResponse对象。

    抛出404错误

    现在,让我们解决问题的详情视图 - 这个页面显示指定投票的问题文本,编辑视图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异常如果一个问题的ID不存在的话。 

    为了快速的展示一下上面的示例,在模板templates/polls/detail.html中输入简单的代码: 

    {{ question }}

    一个快捷方法:get_object_or_404()

    一个常用的语法去使用并且如果对象不存在抛出Http404异常。修改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(),如果列表为空的话抛出404异常。

    使用模板系统

    回到我们投票应用的detail()视图。给一个question的context变量,编辑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 将被解析为python代码question.choice_set.all(),它返回一个Choice迭代对象并且试用于{% for %}模板标签。

    移除在模板中的硬编码网址

    请记住,当我们在polls/index.html 模板编写一个链接时,这个链接是这样写的: 

    <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

    这种硬编码,紧密耦合的问题是,当我们的url需要变化时,将非常麻烦需要修改大量的模板。不过,既然你的polls.urls 中url()方法定义的有名称,那么你可以使用{% url %}模板标签:

    <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

    这种方式是通过查找在polls.urls模块中定义的url来工作的,你可以准确的看到名为“detail”url定义如下: 

    ...
    # 做为{% url %}模板标签的“name”值
    url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
    ...

    如果你想要修改投票的详细视图的url为其他的形式,也许像polls/specifics/12 ,你需要修改它在polls/urls.py 

    ...
    # 增加一个单词 'specifics'
    url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
    ...

    Url名称的命名空间

    本教程的项目仅仅有一个应用,polls。在实际项目中,可能有五个,十个或者更多应用。Django如何区分他们之间的Url名称?例如polls应用有一个detail视图,并且可能同一个项目有一个应用是blog也有一个detail视图。当使用{% url %} 模板标签时Django如何知道url是哪个应用视图的?

    答案是在你的url配置中增加命名空间。在polls/urls.py文件中,增加一个app_name 去设置应用的命名空间 

    from django.conf.urls import url
    
    from . import views
    
    app_name = 'polls'
    urlpatterns = [
        url(r'^$', views.index, name='index'),
        url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
        url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
        url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
    ]

    现在修改你的polls/index.html 模板,从: 

    <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

    改成: 

    <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

     

  • 相关阅读:
    php socket 模型及效率问题
    深入浅出讲解:php的socket通信
    发现一个nginx LUA开发Web App的框架
    centos6.x 抓取ssh登录的用户名和密码
    使用Lua的扩展库LuaSocket用例
    Lua开发
    ngx.location.capture 只支持相对路径,不能用绝对路径
    PHP获得数组的交集与差集
    php用explode,可以提供多个字符作为分割符来进行分割数组吗?
    resizable可调整尺寸组件
  • 原文地址:https://www.cnblogs.com/caoshiqing/p/5843094.html
Copyright © 2020-2023  润新知