zoukankan      html  css  js  c++  java
  • Django 2.0 学习(06):Django 视图(进阶)

    概述

    Django中的特方法,该方法代表了Django的Web页面,并且视图具有特定的模板。以博客应用为例进行说明,在博客应用中应该包含下面的视图:

    • 博客主页:显示最近的一些记录;
    • 详细页面:单个详细页面的入口;
    • 年/月/日:存档页面,显示记录的所有年/月/日;
    • 评论动作:分发评论请求到对应入口;

    在poll应用中,我们将会创建如下视图:

    • Question "index"页面:显示最近的questions;
    • Question "detail"页面:显示问题内容,没有结果的投票表单;
    • Question "results"页面:显示具体question的结果;
    • Vote action:处理具体问题的投票选择;

    在Django中,Web页面或者其他内容都有视图来交付处理,Django通过检查请求的URL来选择对应的视图。关于URL分发处理部分参考URL分发器,或者继续阅读后续博客。

    编写更加丰富的视图

    在我们的polls/view.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 result of question %s."
        return HttpResponse(response % question_id)
    
    
    def vote(request, question_id):
        return HttpResponse("You're voting on question %s." % question_id)
    

    接下来,需要把这些新的视图添加到poll.urls模块中去,添加的方式就是通过调用path()函数,代码如下:

    from django.urls import path
    from . import views
    
    
    urlpatterns = [
        path('', views.index, name='index'),
        path('<int:question_id>/', views.detail, name='detail'),
        path('<int:question_id/results/>', views.results, name='results'),
        path('<int:question_id/vote/>', views.vote, name='vote'),
    ]
    

    在浏览器中进行测试:

    • /polls/34:返回detail()方法对应的内容,其ID是在URL里面提供的;
    • /polls/34/results:显示results页面内容;
    • /polls/34/vote:显示vote页面内容;

    当我们需要访问某个页面时,我们在浏览器里面输入"/polls/34"Django是如何寻找到对应视图并显示出来的呢?下面对这个问题做以回答:

    • 首先,Django会根据settings.py文件的ROOT_URLCONF配置项取加载mystie.urls模块;
    • 其次,在mysite.urls模块中顺序遍历urlpatterns变量;
    • 再次,当匹配到"polls/"时,剥离掉匹配的内容"polls/",携带剩余待匹配内容"34/"进入到"polls.urls"模块,继续进行处理;
    • 最后,遍历polls.urls的patterns变量,匹配到"int:question_id/"进而调用detail()视图函数,其结果像下面:
    detail(request=<HttpRequest object>,  question_id=34)
    

    上面都是对视图函数的简单操作,接下来我们会学习些实际的东西。
    每个视图函数都只做1-2件事情:返回包含请求页面内容的HttpResponse对象,或者抛出Http404异常;视图函数可以读取数据库的记录;可以使用Django自带的模板系统,或者其他第三方Python模板系统;它可以生成PDF,导出XML,创建ZIP文件等,使用Python库函数可以实现一切你想实现的。
    由于它非常的方便,我们就使用Django自带的数据库API来进行演示,在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)
    
    # 保持其他视图函数不变
    

    这里有个问题:Web页面是通过视图函数硬编码方式来设计的。如果我们想更改页面,我们必须修改这里的Python代码,这样做非常的不友好。因此,我们将使用Django的模板系统,来分离页面的设计和Python代码的耦合,取而代之的是让视图函数去使用模板。
    在polls目录下面创建一个目录:templates,Django会自动到该目录里面查找模板文件。其查找机制是:在项目的配置文件settings.py中,"TEMPLATES"定义了Django如何加载和渲染模板;默认配置了一个APP_DIRS为TRUE的DjangoTemplates后台,按照惯例DjangoTemplates会在每个INSTALLED_APPS中查找"templates"这个子目录。

    接下来,在templates目录里面创建空目录:polls,在该空目录里面创建一个index.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 %}
    

    现在,使用模板更新我们之前的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,并且将该模板传递给上下文环境,该上下文环境是一个将模板变量名映射成Pyhton对象的字典;在浏览器中通过"polls"访问页面,如下图所示:

    点击上图的链接,可以看到对应question的详细信息,如下图所示:

    快捷方式:render()
    Django提供了一个快捷方式:rendered template去加载模板,填充上下文环境并且返回HttpResponse对象,详细代码如下所示:

    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对象作为第一个参数,模板名作为第二个参数,上下文环境(字典)作为第三个可选择参数;它将根据给定的模板和上下文环境(字典)返回一个HttpResponse对象。

    抛出404异常
    为了更加丰富我们的视图函数,我们除了对正常情况的处理,还需要对异常进行处理,以视图中的detail()为例进行说明,其代码如下:

    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的异常,为了能够让该实例快速工作,再模板目录下添加polls/detail.html文件,其代码如下所示,具体讲解放在后面:

    {{ question }}
    

    快捷方式:get_object_or_404()
    通过get()获取的对象不存在,并且要抛出Http404异常的时候,Django提供了一种快捷方式,重写下面的detail()函数的代码:

    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异常。
    在Django中还存在一个方法get_list_or_404(),其工作机制如同get_object_or_404(),最大的区别就是前者用filter()代替了后者的get(),并且前者在list为空的时候抛出异常。

    回顾之前创建的detail()视图函数,在polls/detail.html文件中完善模板,代码如下:

    <h1>{{ question.question_text }}</h1>
    <ul>
    {% for choice in question.choice_set.all %}
        <li>{{ choice.choice_text }}</li>
    {% endfor %}
    </ul>
    

    关于模板系统的更多使用,请参考

    最后,我们需要在模板中删除硬编码的URLs
    我们来回顾下polls/index.html模板中的链接的代码,如下所示:

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

    在项目中拥有大量模板文件的时候,这种硬编码、紧耦合方式会存在使得对模板的修改变得异常困难(人力、时间、复杂性);幸运的是,在polls.urls模块的path()方法中,我们定义了关键字参数name,在模板里面我们就可以用{% url %}来替换掉具体的URLs路径,其代码如下:

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

    工作机制:查找polls.urls模块中定义的特定URL,具体见下面代码:

    # name参数的值将会被模板的{% url %}标签调用
    path('<int:question_id>/', views.detail, name='detail'),
    

    当我们想更改detail视图函数对应的URL时,我们可以不需要去修改模板文件,而只需要修改polls/urls.py文件即可,如下所示:

    # 添加前缀单词 'specifics'
    path('specifics/<int:question_id>/', views.detail, name='detail'),
    

    URL名称的命名空间
    在我们的mysite项目中,仅有一个应用:polls。但是,实际的Django项目中,可能会有5个,10个,20个甚至更多的应用。那么Django是如何在这些应用中区分URL的名字呢?举个例子:在polls应用中有个detail视图函数,同样的在该项目的blog中也有个detail视图函数。Django如何知道模板中的{% url %}标签,调用的是哪个应用的视图函数呢?
    答案是:为URLconf添加namespaces。在polls/urls.py文件头部(导入模块下)添加一个app_name来设置应用的命名空间,代码如下所示:

    from django.urls import path
    
    from . import views
    
    app_name = 'polls'
    urlpatterns = [
        path('', views.index, name='index'),
        path('<int:question_id>/', views.detail, name='detail'),
        path('<int:question_id>/results/', views.results, name='results'),
        path('<int:question_id>/vote/', views.vote, name='vote'),
    ]
    

    接下来,修改模板文件polls/index.html(指明detail视图的命名空间),代码如下所示:

    <li>
        <a href="{% url 'polls:detail' question.id  %}">{{ question.question_text }}</a>
    </li>
    
  • 相关阅读:
    GNU make manual 翻译(九十九)
    GNU make manual 翻译( 九十五)
    Shell的 for 循环小例子
    makefile中对目录遍历的小例子
    GNU make manual 翻译(九十三)
    GNU make manual 翻译( 一百)
    GNU make manual 翻译( 九十七)
    GNU make manual 翻译( 九十八)
    mapserver4.8.3 的readme.win32的中文翻译文件
    遥控器编程
  • 原文地址:https://www.cnblogs.com/love9527/p/8608269.html
Copyright © 2011-2022 走看看