Saturday, November 5, 2016

Working with Django

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

See votes 3, 5 are added. Let's format them so we know what's it;s

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


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

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 

 






No comments:

Post a Comment