跟着Django手册建立Blog(二)

本文将指导你使用Django构建Blog应用,包括创建简单的表单form和利用generic views简化代码流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 写一个简单的表单form

a. 改写polls/template/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>
先看效果:
<form action="{% url 'polls:vote' question.id %}" method="post">
这一句告诉浏览器, 使用post方法将form data返回给URL'polls:vote' question.id
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
这行代码为question的每个choice都配置了一个radio按钮, 该按钮的名字是'choice', 该按钮的值关联了choice.id. 当选择了其中一个按钮并按提交按钮时, 将会发送系统一个POST data: choice=id
{{ forloop.counter }}
是指for循环中当前循环次数
{% csrf_token %}
csrf是Cross Site Request Forgeries, 所有POST Form指向内部的URLs时需使用csrf_token语句.

b. 改写vote view处理表单提交的数据
提交了form之后, 需要创建与POST的URL对应的views.
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect
from .models import Question, Choice
from django.urls import reverse

......
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): # KeyError是针对上一句中pk=request.POST来的, 当没有'choice' data时返回错误.
        # 重新显示问题投票表单.
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice.",
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # 在成功处理POST数据后, 需要返回一个HttpResponseRedirect, 防止用户按返回按钮时重复提交两次.
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
request.POST是类似字典的对象, 可以让你通过键名来获取提交的数据. 在此例子中, request.POST['choice']以字符形式返回了选择的choice ID. request.POST的值永远是字符串. 类似的, Django也提供了request.GET来提取GET data.
selected_choice = question.choice_set.get(pk=request.POST['choice'])
reverse()函数目的是为了防止URL的hardcode( 指将可变变量用一个固定值来代替), 该函数格式是 reverse ( viewname , urlconf=None , args=None , kwargs=None , current_app=None ) , 如果views对象可以接受参数传入, 则写在args中.
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

c. 写vote()重定向的result view
在某人在question的detail页面投票之后, form表单内容被提交到'polls:vote'+question.id这个URL, URL调用vote页面, vote页面又重定向到该question的results页面.
在polls/views.py下重写results():
def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/results.html', {'question': question})

d. 写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>
在浏览器输入http://127.0.0.1:8000/polls/1/后, 选择第一个choice后, 点击vote按钮, 则出现以下results页面

如果在投票页面没有选择任何choice就点击vote, 则会出现以下错误页面:
e. 小结, 上述几个URLs--Views--Templates之间的协作关系见下图.



2. 使用generic views简化代码

上述的views的一般套路是: 接收URL并提取参数--从数据库提取数据--用模板渲染数据--以Http响应返回模板.
Django提供generic views系统来替代上述流程.
该系统包括三步: a.转换URLconf,  b.删除没用的views, c.基于generic views引入新的views.

a. 改写polls/urls.py
改之前:
from django.conf.urls import url
from . import views


app_name = 'polls'
urlpatterns = [
    url(r'^$', views.index, name = 'index'),
    url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
    url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]
改之后:
from django.conf.urls import url
from . import views


app_name = 'polls'
urlpatterns = [
    url(r'^$', views.IndexView.as_view(), name = 'index'),  # views.index改为IndexView.as_view()
    url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),  # <question_id>改为<pk>, views.detail改为views.DetailView.as_view()
    url(r'^(?P<pk>[0-9]+)/results/$', views.ResultsView.as_view(), name='results'),  # <question_id>改为<pk>, views.results改为views.ResultsView.as_view()
    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),]
<question_id>改为<pk>是为了配合DetailView从URL中获取主键, 后面会介绍.

b. 改写polls/views.py
改之前:
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect
from django.urls import reverse

from .models import Question, Choice


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)


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


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


def vote(request, question_id):
    # No change.

改写后:
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.views import generic

from .models import Question, Choice


class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    context_object_name = 'latest_question_list'  # 如果不指定, 则默认是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):
    # No change.
ListView和DetailView是两种generic view. ListView是显示对象的清单, DetailView是显示对象的详细内容.

c. 在浏览器中验证, 页面与之前的一样.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值