anonymous
anonymous

Reputation: 1490

How to implement the official django polls example equivalently by using forms?

The offical django polls tutorial is simple, but it has to handle the POST exceptions manually, as well as hardcode the front-end. How to make the same outcome by using forms?

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

I want to ease the process by doing:

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

and {{ form }} in the templates where {{ form }} are able to detect Choice.DoesNotExist and render any error message by default.

Upvotes: 0

Views: 423

Answers (2)

anonymous
anonymous

Reputation: 1490

{##########################################}
{## 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'

This is example how to handle radio forms as backend.

This is pollsForm app equivalently to official polls app, but using forms instead of messing around front-end. It simply generates {{ choice_forms }} to the front-end.

Upvotes: 0

Harper
Harper

Reputation: 1223

First of all you have to define a form. Convention is to do this in forms.py. A form is a Class that inherits from "forms". You can either have forms.Form, where you have to define your Form yourself, or forms.ModelForm, which will generate a form based on a model.

I will provide an example for the Question model to get you started, but you should read this for more input: https://docs.djangoproject.com/en/1.10/topics/forms/

Oh, and if you are looking for "behind the scenes magic" you can look into Class-based view. They handle a lot for you. They are a bit complicated to customize, but there are a few youtube turorials on the topic.

Example for forms.ModelForm:

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

Upvotes: 1

Related Questions