Как реализовать официальный пример опросов django с помощью форм?

Официальное руководство по опросам django очень просто, но оно должно обрабатывать исключения POST вручную, а также жестко программировать интерфейс. Как добиться того же результата с помощью форм?

@csrf_protect
def vote(request, question_id):
    p = get_object_or_404(Question, pk=question_id)
    if request.method == 'GET':
        return render(request, 'polls/detail.html', {
            'question': p,
        })
    if request.method == 'POST':
        try:
            selected_choice = p.choice_set.get(pk=request.POST['choice'])
        except (KeyError, Choice.DoesNotExist):
            # Redisplay the question voting form.
            return render(request, 'polls/detail.html', {
                'question': p,
                'error_message': "You didn't select a choice.",
            })
        else:
            selected_choice.votes += 1
            selected_choice.save()
            return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))



#urls.py
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),


#models.py
class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self):
        return self.question_text

    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)


class Choice(models.Model):
    question = models.ForeignKey(Question)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):
        return self.choice_text



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

Я хочу облегчить процесс, выполнив:

if form.is_valid():
    ...
    votes += 1

а также {{ form }} в шаблонах где {{ form }} умеют определять Choice.DoesNotExist и выводить любые сообщения об ошибках по умолчанию.

2 ответа

Решение
{##########################################}
{## pollsForm/templates/pollsForm/detail.html#}
{##########################################}
{###pay attention to {{choice_forms}}###}

<h1> {{ question.question_text }}</h1>

<form action="{% url 'pollsForm:vote' question.id %}" method="post"> {% csrf_token %}
    {{ choice_forms }}
    <input type="submit" value="Vote" />
</form>

{##########################################}
{## pollsForm/templates/pollsForm/results.html#}
{##########################################}
{#This is optional page. This is from official tutorial##}
<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 'pollsForm:vote' question.id %}">Vote again?</a>   




########################################
# pollsForm/models.py
########################################

same

########################################
# pollsForm/urls.py
########################################

from django.conf.urls import url

from . import views

# namespace='pollsForm'
# append the following line to the myproject/urls.py
#     url(r'^pollsForm/', include('pollsForm.urls', namespace='pollsForm')),

urlpatterns = [
    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
    url(r'^(?P<pk>[0-9]+)/results/$', views.ResultsView.as_view(), name='results'),
]

########################################
# pollsForm/views.py
########################################

from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.views import generic
from django.views.decorators.csrf import csrf_protect
from .models import Choice, Question
from django import forms
from django.forms import widgets
from .forms import ChoiceForm

@csrf_protect
def vote(request, question_id):
    p = get_object_or_404(Question, pk=question_id)
    c_set = Choice.objects.filter(question=question_id)

    if request.method == 'GET':
        cforms = ChoiceForm()
        cforms.fields['choice_text'] = forms.ModelChoiceField(queryset=c_set,
                                                              empty_label=None,
                                                              widget=widgets.RadioSelect)
        variables = {
            'choice_forms': cforms,
            'question': p,
        }
        return render(
            request,
            'pollsForm/detail.html',
            variables,
        )

    if request.method == 'POST':
        form = ChoiceForm(request.POST)
        if form.is_valid():
            pk = form.cleaned_data['choice_text']
            selected_choice = p.choice_set.get(pk=pk)
            selected_choice.votes += 1
            selected_choice.save()
            return HttpResponseRedirect(reverse('pollsForm:results', args=(p.id,)))

        if not form.is_valid():
            # change input char to radio
            form.fields['choice_text'] = forms.ModelChoiceField(queryset=c_set,
                                                                empty_label=None,
                                                                widget=widgets.RadioSelect)
            variables = {
               'choice_forms' : form,
                'question': p,
            }
            return render(
                request,
                'pollsForm/detail.html',
                variables,
            )

# optional
class ResultsView(generic.DetailView):
    model = Question
    template_name = 'pollsForm/results.html'

Это пример того, как обрабатывать радио формы как бэкэнд.

Это приложение pollsForm, аналогичное официальному приложению опросов, но использующее формы вместо того, чтобы возиться с внешним интерфейсом. Он просто генерирует {{ choice_forms }} для внешнего интерфейса.

Прежде всего вы должны определить форму. Соглашение состоит в том, чтобы сделать это в forms.py. Форма - это класс, который наследуется от "форм". У вас может быть либо form.Form, где вы должны определить свою форму самостоятельно, либо forms.ModelForm, которая будет генерировать форму на основе модели.

Я приведу пример для модели Вопросов, чтобы вы начали, но вы должны прочитать это для получения дополнительной информации: https://docs.djangoproject.com/en/1.10/topics/forms/

О, и если вы ищете "закулисную магию", вы можете посмотреть в классовом представлении. Они много для вас справляются. Их немного сложно настроить, но на эту тему есть несколько youtube turorials.

Пример для форм. МоделФорм:

class QuestionForm(forms.ModelForm):

class Meta:
    # Define the model here: 
    model = Question
    # Define the fields that you want to show up. Alternatively you can use "exclude", in which you specify the fields that you dont want to show up.
    fields = [
            "question_text",
            "pub_date",
    ]
    # You can use labels to define custom labels.
    labels = {
            "question_text": "Question Headline",
            }

    # Specify the widgets you want to use, if they differ from the standard widgets defined by the modelField. This is especialy usefull for ForeignKeys.
    #widgets = {
    #    'question': forms.HiddenInput,
    #}
Другие вопросы по тегам