zoukankan      html  css  js  c++  java
  • Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第四部分(Page 9)

    编写你的第一个 Django app,第四部分(Page 9)转载请注明链接地址

    该教程上接前面的第三部分。我们会继续开发 web-poll 应用,并专注于简单的表单处理和简化代码。

    写一个简单的表单(form)

    让我们更新一下我们上个教程编写的的 poll 的 detai 模板(“polls/detail.html”),模板会包含一个 HTML <form> 元素:

    <!--polls/templates/polls/detail.html-->
    <h1>{{ question.question_text }}</h1>
    
    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
    
    <form action="{% url 'polls:vote' question.id %}" method="post">
    {% csrf_token %}
    {% for choice in question.choice_set.all %}
        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
        <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />
    {% endfor %}
    <input type="submit" value="Vote" />
    </form>
    

    简要说明:

    • 上面的模板为每个 question 的 choice 显示了一个单选按钮。每个单选按钮的值对应 quesiton 里 choice 的 ID。单选按钮的名字都是 “choice”。这表示,当有人选择其中一个按钮并提交表单,它会发送 POST 数据 choice=#, “#”表示选中的 choice 的 ID。这是 HTML 中 forms 的基本概念。
    • 我们设置 forms 的 action(应该是行为,但不太确定) 为 {% url 'polls:vote' question.id %} ,并设置 method="post"。使用method="post" (与之相反的是method="get")非常重要,因为这个 form 的提交动作会修改服务端的数据。无论何时,你创建的 form 在修改服务端数据时,都要使用 method="post"。这个技巧并不限于 Django;它是一个很好的 web 开发习惯。
    • forloop.counter 表示 for 标签已经循环了多少次。
    • 我们已经创建了一个POST form(它可以修改用来修改数据),我们需要注意伪装的跨站点请求。幸亏,你不需要太过担心,因为django有一个用来防御它的易于使用的系统。简单的说,所有的POST form 有针对性的在内部URLs中使用 {% csrf_token %}(这里少一个链接)模板标签

    现在,我们创建一个处理提交的数据的 django 视图,并用它搞一些事情。记住,在前面第三部分的教程中,我们为polls应用创建了一个 URLconf,它包含下面一行:

    # polls/urls.py
    path('<int:question_id>/vote/', views.vote, name='vote'),
    

    我们还创建了一个 vote() 函数的虚拟实现。现在我们创建一个真正的。在polls/views.py中添加如下内容:

    # polls/views.py
    from django.shortcuts import get_object_or_404, render
    from django.http import HttpResponseRedirect, HttpResponse
    from django.urls import reverse
    
    from .models import Choice, Question
    # ...
    def vote(request, question_id):
        question = get_object_or_404(Question, pk=question_id)
        try:
            selected_choice = question.choice_set.get(pk=request.POST['choice'])
        except (KeyError, Choice.DoesNotExist):
            # Redisplay the question voting form.
            return render(request, 'polls/detail.html', {
                'question': question,
                'error_message': "You didn't select a choice.",
            })
        else:
            selected_choice.votes += 1
            selected_choice.save()
            # Always return an HttpResponseRedirect after successfully dealing
            # with POST data. This prevents data from being posted twice if a
            # user hits the Back button.
            return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
    

    这里的代码有少量教程中还没有涉及的内容。

    '/polls/3/results/'
    

    3 是 question.id 的值,这个重定向的URL之后会调用 results 视图来显示最终的页面。

    正如前一节教程中提到的,request 是一个 HttpRequest(这里少一个链接) 对象。更多关于 HttpRequest(这里少一个链接)的内容,请查看 request and response documentation(这里少一个链接)
    当有人对一个问题投票后,vote() 视图重定向到问题的结果页面,我们来写一个这个视图:

    # polls/views.py
    from django.shortcuts import get_object_or_404, render
    
    
    def results(request, question_id):
        question = get_object_or_404(Question, pk=question_id)
        return render(request, 'polls/results.html', {'question': question})
    

    它几乎和前一节中的 detail()视图一样。仅有模板名字不一样。稍后我们会修复这个冗余(说是代码重复准确一些)问题。
    现在,创建一个 polls/results.html 模板:

    <!--polls/templates/polls/results.html-->
    <h1>{{ question.question_text }}</h1>
    
    <ul>
    {% for choice in question.choice_set.all %}
        <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
    {% endfor %}
    </ul>
    
    <a href="{% url 'polls:detail' question.id %}">Vote again?</a>
    

    现在,我们在浏览器中打开 “/polls/1/”并给这个问题投票。你应该可以看到每次投票结果更新后的页面。如果你提交的form中没有选中的choice,你会看到一个错误消息。

    注意
    代码中我们的vote()视图有一个小小的问题,它首先从数据库中得到一个selected_choice 对象,然后计算新的投票结果,并将结果保存到数据库。如果你的站点有两个用户尝试在同一个时间点投票,这可能会导致错误: 相同的值,比如被取回来的投票数是42。然后两个用户都会43这个新值并被保存,而不是预期的值44.
    这个叫做竞态条件(race condition:指设备或系统出现不恰当的执行时序,而得到不正确的结果)。如果你感兴趣。你可以阅读 使用F()避免静态条件(这里少一个链接),学习如何解决这个问题。

    使用通用视图(Generic views):代码还是少点好

    detail() 和 results()视图都很简单 —— 并且,像上面提到的一样,冗余(代码重复)。和index()视图类似,显示投票题目的一个列表。
    这些视图反应了web开发中一个常见情况:根据URL中传递的参数从数据库获取数据,记在模板并返回渲染后的模板。由于这个太常见,Django提供了一个快捷的,叫做“通用视图”的系统。
    通用视图抽象常见的模式, 可以让你编写app时甚至不需要写python代码。

    让我们讲poll app转换成通用视图,这样我们可以删除许多代码。我们仅需要几步来完成转换。我们会:

    1. 调整URLconf
    2. 删除一些旧的,没用的视图
    3. 引入基于Django通用视图的新视图

    详情请阅读。

    为什么重构代码? (code-shuffle:我理解的是代码重构)
    一般,当我们写Django app,你需要评估通用视图是否适合解决你的问题,你可以从一开始就使用它,不是半途重构你的代码。但到现在为止,本教程有意专注使用“困难的方式”编写视图,把重点放在核心概念上。
    使用计算器之前,你应该对数学有一个基本的了解。

    改进 URLconf

    首先,打开 polls/urls.py ,像下面一样修改 URLconf。

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

    注意第二个和第三个路径字符串里匹配模式的名字从 <question_id> 变成了 <pk>

    改进视图

    下一步,我们会移除旧的 indexdetailresults 视图,并用Django的通用视图替换它们。 这需要打开 polls/views.py 文件将它改成类似下面的样子:

    # polls/views.py
    from django.shortcuts import get_object_or_404, render
    from django.http import HttpResponseRedirect
    from django.urls import reverse
    from django.views import generic
    
    from .models import Choice, Question
    
    
    class IndexView(generic.ListView):
        template_name = 'polls/index.html'
        context_object_name = 'latest_question_list'
    
        def get_queryset(self):
            """Return the last five published questions."""
            return Question.objects.order_by('-pub_date')[:5]
    
    
    class DetailView(generic.DetailView):
        model = Question
        template_name = 'polls/detail.html'
    
    
    class ResultsView(generic.DetailView):
        model = Question
        template_name = 'polls/results.html'
    
    
    def vote(request, question_id):
        ... # same as above, no changes needed.
    

    这里我们使用了两个通用视图: ListVIew(这里少一个链接)DetailView(这里少一个链接)。这两个视图分别抽象了 “显示一个对象的列表” 和 “显示一个特定类型对象的详情页” 的概念。

    • 每个通用视图需要知道她将作用于那个模型。这由使用的模型的属性提供。
    • DetailView(这里少一个链接) 通用视图期望从URL捕捉到的名为“pk”的主键值,所以我们将question_id 改成 pk 来使通用视图可以找到它。

    默认情况下,DetailView(这里少一个链接)通用视图使用一个名为 <app name>/<model name>_detail.html的模板。在我们的例子中,我们使用 "polls/question_detail.html"模板。template_name属性用于告诉Django使用特定的模板名去替换自动生成的默认模板名,我们还需要为 result 列表视图指定 template_name —— 这是为确保result视图和detail视图在渲染时呈现不同的外观,虽然它们后面是同一个 DetailView(这里少一个链接)视图。

    类似的,ListVIew(这里少一个链接)通用视图使用了名为 <app name>/<model name>_detail.html 的模板。我们使用 template_name 告诉 ListVIew(这里少一个链接) 去使用我们已经有的 "polls/index.html"模板。
    在教程前面的部分,已经提供了一个包含 questionlatest_question_list 的 context 变量的模板。对DetailView来说, question变量已经被自动提供 —— 从我们使用模型(Question)开始,Django可以为context变量取一个合适的名字。然而,对于 ListView,自动生成的context变量是 question_list。重写我们提供的 context_object_name 属性,用我们想使用的 latest_question_list 替代它。作为一种替代方法,你可以更改你的模板来匹配新的默认的context变量 —— 然而直接告诉Django去使用你想用的变量会简单很多。

    运行服务器,使用新的基于通用视图的投票app。
    更多关于通用视图的内容,请参阅 [通用视图文档](这里少一个链接)()。

    当你熟悉了表单和通用视图,可以继续向下学习 测试我们的投票app的内容。

  • 相关阅读:
    JSP——隐式对象(implicit object)
    Spring——原理解析利用反射和注解模拟IoC的自动装配
    Spring操作指南针对JDBC配置声明式事务管理(基于注解)
    SpringMVC操作指南登录功能与请求过滤
    Spring操作指南针对JDBC配置声明式事务管理(基于XML)
    Flex Tree 限制只能同级拖动 获得拖动前后节点信息
    坐标系、坐标参照系、坐标变换、投影变换【转】
    国内地图偏移的一些知识
    Web Mercator
    WKT【转】
  • 原文地址:https://www.cnblogs.com/resn/p/8353559.html
Copyright © 2011-2022 走看看