Django学习4:form, generic views

Django学习4

 在经过前三节的学习后,基本了解了数据库的连接,views的使用,但是还需要了解如何传回数据并处理,这里第四节学习的内容就是如何去用form来获取数据。

step1:熟悉form

 将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>

 这其中需要解释的是forloop.counter是表示当前循环进行了几次。而{% csrf_token %}是一个Django 提供的template标记用来防止跨站伪造请求。
 这之后由于表单提交给了vote页面,所以去重写vote的view来达到能够登记提交的查询的目的。
 题外话,这里可以探讨一下id和name的作用,可以参考 stackoverflow the difference between id and name
 在vote这个view中就要实现:接收信息,查询信息,更新信息,显示结果页面,代码如下:

from django.shortcuts import render, get_object_or_404
# Create your views here.
from django.http import HttpResponse, HttpResponseRedirect
from .models import Question
from django.template import loader
from django.http import Http404
from django.urls import reverse


def vote(request, question_id):
    #request直接接收信息
    question = get_object_or_404(Question, pk=question_id)
    try:
        #用get来查询是否存在choice,并且raise自己定义的error_message
        select_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:
        #更新数据并且重定向到result页面
        select_choice.vote += 1
        select_choice.save()
        return HttpResponseRedirect(
            reverse('polls:results', args=(question.id,))
        )

 上述代码中有一些需要解释的代码:

  1. request.post是一个字典型的数据,可以通过key name来访问传入的数据,需要注意的是其中的数据皆为string类型
  2. request.post['choice']如果没有在传入数据中有有效值的情况下,会raise一个keyerror错误,当出现这个错误的时候,本代码的处理方式就是重新返回detail.html并给出错误信息。
  3. 当处理完成post数据后,一般都会使用HttpResponseRedirect()来重定位页面。这里使用了reserve这个函数来定向到result这个view并且给出了question_id,也就是说这里的reserve('polls:result', args=(question.id))相当于重定向到了"polls/question.id/results"页面。

 重写一下result view来满足需求:

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

 添加一个result的页面:

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

 上述代码每次就会显示每个choice的选择情况。值得注意学习的是这里的{{ choice.votes|pluralize }}是根据votes的情况来加复数"s"。
 需要补充的是,这个程序是有问题的,当两个人同时使用这个vote时,会导致数据的错误,可以使用F()函数来避免这一问题,详情见F()

step2:通用view

 我们可以看到在detail中和results中的view的代码大同小异,此时我们就可以来简化代码,所以我们可以利用Django来做一个通用类的template来简化代码。
 我们需要经历以下步骤:

  1. 改变urls的配置
  2. 删除老的无用代码
  3. 使用新的generic views

 那么首先,需要对polls/urls.py进行修改:

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'),
]

 而后修改polls/views.py文件为:

from django.views import generic


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'

 上述代码利用了两个generic view,分别是Listview和DetailView。他们分别用于显示一个对象的list和显示一个具体类型的对象的detail页面。
 每一个generic都需要知道它所作用的model,所以提供了model属性来指明model。
 DetailView需要一个叫做pk的主键,所以在urls.py中我们使用pk来代替question_id。
 在DetailView中,通常会使用"< app bane>/< model name>_detail.html"不过我们这里用template_name来指定了页面,同理ListView。
 在DetailView中,当我们使用了model:Question,就会自动命名object的名字是question,然后我实验了ListView好像也是自动提供的,(把question改名程question2就不行了)。同时DetailView还会自动提供context的名字为question_list不过我们可以自己用context_objetc_name来改变名字。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值