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

    Django 2.0 学习(06):Django 视图(进阶),我们将聚焦在使用简单的表单进行处理和精简代码。

    编写简单表单

    我们将用下面的代码,来替换之前的detail模板("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>
    

    快速理解:

    • 上述模板为每个问题选项添加了一个单选按钮,该按钮的值与问题选项的ID关联,每个按钮的名字都是"choice".这就意味着,当有人选择某个单选按钮并且提交的时候,选择的单选按钮会发送POST数据choice=#,其中#就是选择的choice;
    • 设置表单的action为{% url 'polls:vote' question.id %},设置method="post";这部分内容已经超出Django的范畴,属于Web开发的范畴;
    • 在使用POST表单时,需要考虑到跨站点请求伪造(Cross Site Request Forgeries).幸运的是,我们不需要考虑的太多,因为Django已经帮我们处理的很好了。简言之,我们所需要做的是:在POST表单请求里面添加{% csrf_token %}模板标签。

    接下来,创建视图函数来对提交的数据进行处理。编辑polls/views.py,其代码如下:

    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):
            return render(request, 'polls/detail.html', {
                'question': question,
                'error_message': "You didn't select a choice."
            })
        else:
            selected_choice.votes += 1
            selected_choice.save()
            return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
    

    上述代码涉及部分内容做如下解释:

    • request.POST是个像字典的对象,可以通过该对象的关键字(key)来获取提交数据。在这里,request.POST['choice']返回被选择选项的ID,request.POST返回值总是字符串;
    • 如果在POST数据中没有提供choicerequest.POST['choice']将会抛出KeyError异常。如果未提供choice,上述代码会检查KeyError并且通过一个错误信息来重新显示该问题;
    • 在增加选择计数后,代码返回HttpResponseRedirct而不是HttpResponse.HttpResponseRedirect携带一个单独的参数:用户被重定向到哪里的URL;作为优秀的Web开发,POST请求成功应该总是返回HttpResponseRedirect,这并不是Django特别要求的;
    • HttpResponseRedirect构造其中,我们使用了reverse()方法。该方法避免在视图函数中使用URL硬编码,他会提供我们想传递的视图函数的名字并且在ULR模式中找到该视图。

    在有人对某个问题投票后,vote()视图会把结果重定向到结果页面,下面代码是完善的视图函数:

    def results(request, question_id):
        question = get_object_or_404(Question, pk=question_id)
        return render(request, 'polls/results.html', {'question': question})
    

    现在创建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>
    

    request就是个HttpRequest对象,更多信息详见HttpRequest

    现在,在浏览器中访问/polls/1/并且对问题进行投票。我们会看到如下页面:

    如果没有选择任何选项,会看到错误信息,如下图所示:

    注:上面的vote()视图有个小小的问题。该函数第一次从数据库中获取selected_choice对象,接下来计算新的votes的值,并且将其保存回数据库中。同一时刻,如果有两个用户都发起投票,就会出现下面的错误:将会得到相同的votes值,假设现在的值是42,然而对两个用户来说43这个新的值将会被计算和保存,但是我们期望的值是44。

    通用视图:更少的代码更好
    上述视图函数代表了基本Wbe开发的一种通用情况:根据URL传递的参数从数据库中获取数据,加载模板并返回渲染后的模板。因为这是公共的,所以Django提供了一种快捷方式,称之为"通用视图"系统。

    让我们使用通用视图系统来改造之前的代码,其步骤如下:
    1、改变URLconf;
    2、删除旧的、非必要的视图函数;
    3、基于Django的通用视图,引入新的视图;

    首先,修改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

    其次,修改视图函数(使用Django的通用视图系统替换旧的indexdetailresults视图):打开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 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):
        question = get_object_or_404(Question, pk=question_id)
        try:
            selected_choice = question.choice_set.get(pk=request.POST['choice'])
        except (KeyError, Choice.DoesNotExist):
            return render(request, 'polls/detail.html', {
                'question': question,
                'error_message': "You didn't select a choice."
            })
        else:
            selected_choice.votes += 1
            selected_choice.save()
            return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
        # return HttpResponse("You're voting on question %s." % question_id)
    

    我们这里使用两个通用视图:ListViewDetailView,这两个视图分别代表了"显示对象的列表"和"显示某个对象的详细信息"。

    • 使用模型属性,每个通用视图需要知道它所作用的模型;
    • DetailView通用视图期望主键值能通过URL中获取到,所以在通用视图中我们把question_id改成pk

    默认情况,DetailView通用视图使用一个叫做app name/model name_detail.html的模板。在我们的示例中,将会使用模板"polls/question_detail.html"template_name属性用来告诉Django使用一个具体的名字代替自动生成的默认的模板名字。
    同样的,ListView通用视图使用默认模板app name/model name_list.html;我们使用template_name告诉ListView使用我们的polls/index.html模板。

  • 相关阅读:
    记录下python学习中,容易弄混和实用的知识点
    操作系统简史
    计算机结构
    计算机结构
    电脑简史
    电脑简史
    为什么学Python
    为什么学Python
    树莓派更换更新国内源
    树莓派更换更新国内源
  • 原文地址:https://www.cnblogs.com/love9527/p/8617889.html
Copyright © 2011-2022 走看看