Python Web开发-Django2.0学习05

五、表单和通用视图

1、表单

Django提供的表单相比HTML表单可以对数据进行更加复杂的验证工作,重写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>

PS:上报’polls’ is not a registered namespace错误,需要在polls/urls.py增加以下代码:

app_name = 'polls'

error_message为当你未选择任一单选按钮就提交表单时,提示错误。

表单包含两个属性:action和method。action为当你提交表单时,向何处发送表单数据。method为提交表单的方式,有get和post两种。后面还有一个{% csrf_token %}标签,csrf全称是Cross Site Request Forgery,这是Django提供的防止伪装提交请求的功能。post方法提交表单时,必须有此标签。

forloop.counter表示for标签已经经过了多少次循环。

label的for属性规定label与哪个元素绑定。

从表单上action属性可知,将由vote/question.id页面处理表单数据,需要重写polls/views.py里的vote()函数,代码如下:

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

request.POST可以看成一个字典,可以通过key去获取提交的数据。request.POST[‘choice’] 获取你选取的radio的value值choice.id,并保存为string。这也可以用request.GET.get(‘key’, None)来取值,没有时不报错,request.GET类似。

增加选择计数后,代码返回一个HttpResponseRedirect而不是一个正常的HttpResponse。 HttpResponseRedirect只有一个参数:用户将被重定向到的URL。原因见注释。在成功处理POST数据之后,应该总是返回一个HttpResponseRedirect。

reverse()函数可以避免在视图功能中硬编码URL,参数为想要跳转的页面的视图名称以及指向该视图的URL模式的可变部分。在这里,这个reverse()调用将会返回一个类似的字符串’/polls/3/results/’。

某人投票后,vote()函数将会跳转到结果页面,需要重写polls/views.py的results()函数,代码如下:

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})

增加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>

PS.我们代码中是从数据库中获取对象,然后计算新的值votes,然后将其保存回数据库。这里有个问题。如果您的网站的两个用户尝试在同一时间投票,这可能会出错:相同的值,比如说42,将被检索votes。然后,为两个用户计算并保存43的新值,但44将是期望值。这种情况被称为race condition,处理方法见Avoiding race conditions using F()

2、使用通用视图:代码越少越好

Django的通用视图抽象出一些在视图开发中常用的代码和模式,这样就可以在无需编写大量代码的情况下,快速编写出常用的数据视图。

Django内建通用视图可以实现如下功能:

  • 完成常用的简单任务: 重定向到另一个页面以及渲染一个指定的模板。
  • 显示列表和某个特定对象的详细内容页面。
  • 呈现基于日期的数据的年/月/日归档页面,关联的详情页面,最新页面。Django Weblogs(http://www.djangoproject.com/weblog/)的年、月、日的归档就是使用通用视图架构的,就像是典型的新闻报纸归档。

综上所述,这些视图为开发者日常开发中常见的任务提供了易用的接口。下面主要介绍DetailView和ListView通用视图的使用,想了解更多信息请参考Built-in class-based views API

声明视图类

将旧的index、detail和results视图函数注释,在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这两个视图分别抽象出“显示对象列表”和“显示特定类型对象的详细页面”的概念。

model属性用于绑定模型,说明获取数据。

template_name属性用于说明使用特定的模板名称而不是自动生成的默认模板名称,确保结果视图和细节视图在呈现时具有不同的外观。

URL配置

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

第二个和第三个path的路径字符串已从更改为。因为DetailView通用视图将从URL中捕获的主键值称为 “pk”,所以在通用视图中,需要将question_id改以pk。


参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

森森向上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值