Today I was trying tutorial from django here is basic reference of it : https://docs.djangoproject.com/en/1.10/intro/tutorial04/
Here is how my detail.html was looking
Just to ensure that I have learned stuff and not copied, Have added few more scenarios:
1. Let's add current vote values over here, Not much value addition but worth try.
This is pretty straight forward, will simply add another label with value as Votes instead of choicename
Our original code is
<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>
To solve we just copy line for choice_text and replace it with choice.votes (remember that s, and do remove that breakline so that it looks neat)
So new code becomes:
<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>
<label for="choice{{ forloop.counter }}">{{ choice.votes}}</label><br />
{% endfor %}
<input type="submit" value="Vote" />
</form>
Which looks like
<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 }}` <i> with Current votes <u> {{choice.votes}} </u>
</i> </label> <br/>
{% endfor %}
<input type="submit" value="Vote" />
</form>
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,)))
Problem is with request.POST['choice'], it's only reading last choice ignoring earlier once, Solution is to replace request.POST['choice'] with request.POST.getlist('choice').
Notice that change from square brackets to round once, this is because we were using dictionary with key as 'choice' now instead of dict we are calling function of Python getlist hence round brackets.
But now that won't solve complete issue
Here is error we are getting:
If we scroll down we see actual issue:
Problem is we were passing array to function question.choic_set.get(). So we just write a for loop which will iterate over array and save each value
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
for selected_choice_pk in request.POST.getlist('choice'):
selected_choice = question.choice_set.get(pk=selected_choice_pk)
selected_choice.votes += 1
selected_choice.save()
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:
# 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,)))
Wow issue resolved
Update:
Found issue original code was return such that if no choices are selected error is raise, with our changes it's not getting in.
Solution:
First add dummy call to check if they are any return values: request.POST.getlist('choice')[0]
What will this do is raise a IndexError then So we simply catch it :
except (KeyError, Choice.DoesNotExist, IndexError) as e:
# Redisplay the question voting form.
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice.",
})
Also we can delete our earlier enhancement #1 once result page is there
Few more update:
Another issue, what if there are no choices for given Question,
Solution #1: Check if choices are there if not provide back link
This is just simple if else and URL tags
{% if question.choice_set.count > 0 %}
<form action="{% url 'polls:vote' question.id %}" method="post">
{% for choice in question.choice_set.all %}
<input type="checkbox" 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>
{% else %}
<p> <strong style="color:RED;"> Error there are no choices availabe for given question, Report to admin to add choices</strong>
<a href="{{request.META.HTTP_REFERER}}">Go back</a> or <a href="{% url 'polls:index' %}"> Go to Index </a>
{% endif %}
Solution #2: Modify Index to show only questions with Choices else don't show them at all / show with Mark saying there are no choices
Here is how my detail.html was looking
Just to ensure that I have learned stuff and not copied, Have added few more scenarios:
1. Let's add current vote values over here, Not much value addition but worth try.
This is pretty straight forward, will simply add another label with value as Votes instead of choicename
Our original code is
<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>
To solve we just copy line for choice_text and replace it with choice.votes (remember that s, and do remove that breakline so that it looks neat)
So new code becomes:
<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>
<label for="choice{{ forloop.counter }}">{{ choice.votes}}</label><br />
<input type="submit" value="Vote" />
</form>
Which looks like
See votes 3, 5 are added. Let's format them so we know what's it;s
{% 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 }}` <i> with Current votes <u> {{choice.votes}} </u>
</i> </label> <br/>
{% endfor %}
<input type="submit" value="Vote" />
</form>
Here is output:
2. Let's replace these radio buttons with check boxes.
That's even simpler modify type as "checkbox" instead of radio, but results are not as expected.
Did you catch that, yes only `Midnoght` is getting incremented, that's code is only get last choice and not all choices, :(
Let's see what's happening, in django details.html is only loading page but core work of adding is done by our view .
mysites/polls/view.py
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,)))
Problem is with request.POST['choice'], it's only reading last choice ignoring earlier once, Solution is to replace request.POST['choice'] with request.POST.getlist('choice').
Notice that change from square brackets to round once, this is because we were using dictionary with key as 'choice' now instead of dict we are calling function of Python getlist hence round brackets.
But now that won't solve complete issue
Here is error we are getting:
If we scroll down we see actual issue:
Problem is we were passing array to function question.choic_set.get(). So we just write a for loop which will iterate over array and save each value
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
for selected_choice_pk in request.POST.getlist('choice'):
selected_choice = question.choice_set.get(pk=selected_choice_pk)
selected_choice.votes += 1
selected_choice.save()
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:
# 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,)))
Wow issue resolved
Update:
Found issue original code was return such that if no choices are selected error is raise, with our changes it's not getting in.
Solution:
First add dummy call to check if they are any return values: request.POST.getlist('choice')[0]
What will this do is raise a IndexError then So we simply catch it :
except (KeyError, Choice.DoesNotExist, IndexError) as e:
# Redisplay the question voting form.
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice.",
})
Also we can delete our earlier enhancement #1 once result page is there
Few more update:
Another issue, what if there are no choices for given Question,
Solution #1: Check if choices are there if not provide back link
This is just simple if else and URL tags
{% if question.choice_set.count > 0 %}
<form action="{% url 'polls:vote' question.id %}" method="post">
{% for choice in question.choice_set.all %}
<input type="checkbox" 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>
{% else %}
<p> <strong style="color:RED;"> Error there are no choices availabe for given question, Report to admin to add choices</strong>
<a href="{{request.META.HTTP_REFERER}}">Go back</a> or <a href="{% url 'polls:index' %}"> Go to Index </a>
{% endif %}
Solution #2: Modify Index to show only questions with Choices else don't show them at all / show with Mark saying there are no choices
No comments:
Post a Comment