Python Lover(3)Django Doc - POST FORM and Testing

Python Lover(3)Django Doc - POST FORM and Testing

1. Form
How the POST Form Working
<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>

We have the csrf_token to handle the security issue.

The Result Page
<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 'polls:detail' question.id %}">Vote again?</a>

The Action-View 
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.shortcuts import get_object_or_404
from django.core.urlresolvers import reverse

from polls.models import Question, Choice

def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/results.html', {'question': question})

def vote(request, question_id):
    p = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = p.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        return render(request, 'polls/detail.html', {
            'question': p,
            'error_message': "You did not select a choice.",
        })
    else:
        selected_choice.votes +=1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))

Use Generic Views: Less Code
I do not like it, I want more control of the codes. 
https://docs.djangoproject.com/en/1.7/intro/tutorial04/

2. Automated Testing
2.1 Try to Creating Test Class

Find the file easypoll/polls/tests.py

import datetime

from django.test import TestCase
from django.utils import timezone

from polls.models import Question


# Create your tests here.
class QuestionMethodTests(TestCase):

    def test_was_published_recently_with_future_question(self):
        """
        was_published_recently() should return False for questions whose
        pub_date is in the future
        """
        time = timezone.now() + datetime.timedelta(days=30)
        future_question = Question(pub_date=time)
        self.assertEqual(future_question.was_published_recently(), False)

    def test_was_published_recently_with_old_question(self):
        """
        was_published_recently() should return False for questions whose
        pub_date is older than 1 day
        """
        time = timezone.now() - datetime.timedelta(days=30)
        old_question = Question(pub_date=time)
        self.assertEqual(old_question.was_published_recently(), False)

    def test_was_published_recently_with_recent_question(self):
        """
        was_published_recently() should return True for questions whose
        pub_date is within the last day
        """
        time = timezone.now() - datetime.timedelta(hours=1)
        recent_question = Question(pub_date=time)
        self.assertEqual(recent_question.was_published_recently(), True)

It is easy to run the Tests
>python manage.py test polls
Creating test database for alias 'default'... . ---------------------------------------------------------------------- Ran 1 test in 0.001s

2.2 Test a View
The Django Test Client
We can use it even from the Console, that is amazing.
>python manage.py shell

Prepare the Context, then we can parse the response or something like that from my understanding.
>>> from django.test.utils import setup_test_environment >>> setup_test_environment()

Set up the Client
>>> from django.test import Client >>> client = Client()

Using the client to talk to Our Server
>>> response = client.get('/') >>> response.status_code 404

Reverse the URL from Server Conf
>>> from django.core.urlresolvers import reverse >>> response = client.get(reverse('polls:index')) >>> response.status_code 200
>>> response.content b'\n    <ul>\n    \n        <li><a href="/polls/2/">National</a></li>\n    \n        <li><a href="/polls/1/">what&#39;s up?</a></li>\n    \n    </ul>\n'

Directly hit the URL
>>> response = client.get('/polls/') >>> response.content b'\n    <ul>\n    \n        <li><a href="/polls/2/">National</a></li>\n    \n        <li><a href="/polls/1/">what&#39;s up?</a></li>\n    \n    </ul>\n'

Directly check the context, that is really amazing and you do not need to have your app running.
>>> response.context['latest_question_list'] [<Question: National 2014-09-15 19:23:15+00:00>, <Question: what's up? 2014-09-15 17:25:50+00:00>]

Not from the Shell, Write in tests.py
from django.test import TestCase
from django.utils import timezone
from django.core.urlresolvers import reverse

from polls.models import Question

#public static method?
def create_question(question_text, days):
    """
    Creates a question with the given `question_text` published the given
    number of `days` offset to now (negative for questions published
    in the past, positive for questions that have yet to be published).
    """
    time = timezone.now() + datetime.timedelta(days=days)
    return Question.objects.create(question_text=question_text,
                                   pub_date=time)

Test Classes
class QuestionViewTests(TestCase):
    def test_index_view_with_no_questions(self):
        """
        If no questions exist, an appropriate message should be displayed.
        """
        response = self.client.get(reverse('polls:index'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "No polls are available.")
        self.assertQuerysetEqual(response.context['latest_question_list'], [])

    def test_index_view_with_a_past_question(self):
        """
        Questions with a pub_date in the past should be displayed on the
        index page
        """
        create_question(question_text="Past question.", days=-30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(
            response.context['latest_question_list'],
            ['<Question: Past question.>']
        )

    def test_index_view_with_a_future_question(self):
        """
        Questions with a pub_date in the future should not be displayed on
        the index page.
        """
        create_question(question_text="Future question.", days=30)
        response = self.client.get(reverse('polls:index'))
        #print(response.content)
        self.assertContains(response, "No polls are available.",
                            status_code=200)
        self.assertQuerysetEqual(response.context['latest_question_list'], [])

    def test_index_view_with_future_question_and_past_question(self):
        """
        Even if both past and future questions exist, only past questions
        should be displayed.
        """
        create_question(question_text="Past question.", days=-30)
        create_question(question_text="Future question.", days=30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(
            response.context['latest_question_list'],
            ['<Question: Past question.>']
        )

    def test_index_view_with_two_past_questions(self):
        """
        The questions index page may display multiple questions.
        """
        create_question(question_text="Past question 1.", days=-30)
        create_question(question_text="Past question 2.", days=-5)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(
            response.context['latest_question_list'],
            ['<Question: Past question 2.>', '<Question: Past question 1.>']
        )

class QuestionIndexDetailTests(TestCase):
    def test_detail_view_with_a_future_question(self):
        """
        The detail view of a question with a pub_date in the future should
        return a 404 not found.
        """
        future_question = create_question(question_text='Future question.',
                                          days=5)
        response = self.client.get(reverse('polls:detail',
                                   args=(future_question.id,)))
        self.assertEqual(response.status_code, 404)

    def test_detail_view_with_a_past_question(self):
        """
        The detail view of a question with a pub_date in the past should
        display the question's text.
        """
        past_question = create_question(question_text='Past Question.',
                                        days=-5)
        response = self.client.get(reverse('polls:detail',
                                   args=(past_question.id,)))
        self.assertContains(response, past_question.question_text,
                            status_code=200)

Tips
gte, lte seems to me, it is less, greater.

You do not need to have your app running when you doing the tests. Run the test like this
>python manage.py test polls
Creating test database for alias 'default'... .......... ---------------------------------------------------------------------- Ran 10 tests in 0.039s

Some Issues and Fixes
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.shortcuts import get_object_or_404
from django.core.urlresolvers import reverse
from django.utils import timezone
from django.http import Http404

from polls.models import Question, Choice



# Create your views here.
def index(request):
    latest_question_list = Question.objects.filter(
        pub_date__lte = timezone.now()
    ).order_by('-pub_date')[:5]
    #latest 5 records
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)

def detail(request, question_id):
    try:
        question = Question.objects.filter(
            pub_date__lte = timezone.now()
        ).get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404
    return render(request, 'polls/detail.html', {'question': question})

References:
https://docs.djangoproject.com/en/1.7/intro/tutorial04/
https://docs.djangoproject.com/en/1.7/intro/tutorial05/

More Topic
https://docs.djangoproject.com/en/1.7/topics/

How to deploy
https://docs.djangoproject.com/en/1.7/howto/deployment/

Spawn-fcgi, wsgi, fastcgi, cgi
http://lihuipeng.blog.51cto.com/3064864/890573

猜你喜欢

转载自sillycat.iteye.com/blog/2117212