среда, 7 мая 2014 г.

Опрос (ч.5)

В части 4 мы создали роут, по которому стали доступен список последних двух опросов. В этой части мы сделаем детальное отображение опроса.

1. Добавим роут в файл polls/urls.py:
# ...
urlpatterns = patterns('',

    # ...
    url(r'^(?P\d+)/$', PollDetailView.as_view(), name='detail'),
)

Это значит, что представление PollDetailView будет вызываться, когда пользователь переходит, к примеру, по адресу: http://127.0.0.1:8000/polls/1

2. Создадим представление в файле polls/views.py:
# -*- coding: utf-8 -*-
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from .models import Question, Answer

# ...

class QuestionDetailView(DetailView):
    model = Question
    template_name = 'polls/detail.html'

Здесь мы создали класс представления, который расширяет встроенный в django класс DetailView, содержащий нужный функционал для отображения детальной страницы.

3. Детальное представление опроса должно быть формой с радиокнопками, чтобы ответ пользователя можно было отправить на определенный url, при обращению к которому будет вызвано представление, где данные запишутся в БД. Создадим такой шаблон по адресу polls/templates/polls/detail.html:

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

{% if error_message %}
    <p><strong>{{ error_message }}</strong></p>
{% endif %}

<form action="" method="post">
    {% csrf_token %}
    {% for answer in question.answer_set.all %}
        <input type="radio" name="answer" id="answer{{ forloop.counter }}" value="{{ answer.id }}" />
        <label for="answer{{ forloop.counter }}">{{ answer.answer }}</label><br />
    {% endfor %}
    <input type="submit" value="Голосовать" />
</form>

Обратите внимание на выражение: question.answer_set.all

Вот таким образом в шаблоне можно получить список всех элементов связанной таблицы. В нашем случае у каждого Вопроса есть несколько Ответов. При помощи приведенного выражения мы можем по экземпляру вопроса получить список соответствующих ему ответов.


Перейдем по адресу http://127.0.0.1:8000/polls/1/ и увидим результат:


4. Однако приведенная форма пока не работает.Чтобы она заработала, нужно добавить роут и представление для обработки POST-данных отправляемой формы. Создадим новый роут в файле polls/urls.py:
urlpatterns = patterns('',
    # ...
    url(r'^(?P\d+)/vote/$', vote, name='vote'),
)

5. Создадим представление, но уже не в виде класса, а в виде функции. Отредактируем файл polls/views.py:
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.views.generic import ListView, DetailView
from .models import Question, Answer

# ...

def vote(request, poll_id):

    question = get_object_or_404(Question, pk=poll_id)
    
    if request.POST.get('answer'):
        selected_answer = question.answer_set.get(pk=request.POST['answer'])
        selected_answer.votes += 1
        selected_answer.save()
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
    else:
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "Вы не выбрали ответ.",
        })

Здесь мы проверяем наличие переменной answer в POST-запросе. Если она есть, мы по коду ответа достаем запись из базы данных и увеличиваем счетчик votes на 1. После этого делаем редирект на страницу результатов (пока еще не создали).

6. Добавим новый роут страницы результатов опроса в polls/urls.py:
urlpatterns = patterns('',
    # ...
    url(r'^(?P\d+)/results/$', ResultsView.as_view(), name='results'),
)

7. Для созданного роута создадим представление в polls/views.py:
# ...
class ResultsView(DetailView):
    model = Question
    template_name = 'polls/results.html'

8. Для созданного представления создадим шаблон polls/templates/polls/results.html:
<h1>{{ question.title }}</h1>

<ul>
{% for answer in question.answer_set.all|dictsortreversed:"votes" %}
    <li>{{ answer.answer }} - голосов: {{ answer.votes }}</li>
{% endfor %}
</ul>

<a href="{% url 'polls:detail' question.id %}">Голосовать еще раз</a>

Здесь мы при помощи фильтра Django dictsortreversed:"votes" сортируем ответы в порядке убывания числа голосов.

Выражение {% url 'polls:detail' question.id %} позволяет сформировать ссылку на детальную страницу просмотра опроса, где находится форма голосования. Здесь polls - неймспейс, указанный при подключении роутов приложения polls в главном файле роутов проекта firstsite/urls.py:
urlpatterns = patterns('',
    # ...
    # Задаем неймспейс "polls" для импортируемых роутов приложения 
    url(r'^polls/', include('polls.urls', namespace="polls")),
)

9. Проверим работу, перейдя в опрос: http://127.0.0.1:8000/polls/1/
Там мы увидим форму голосования. После выбора варианта ответа и нажатия "Голосовать" мы будем перенаправлены на страницу результатов опроса:

Обратите внимание: ответы отсортированы в порядке убывания голосов.

Продолжение - Опрос (ч.6)

Комментариев нет:

Отправить комментарий