Loading docs/intro/tutorial04.txt +42 −28 Original line number Diff line number Diff line Loading @@ -185,7 +185,7 @@ conversion. We will: 2. Delete some of the old, unneeded views. 3. Fix up URL handling for the new views. 3. Introduce new views based on Django's generic views. Read on for details. Loading @@ -205,32 +205,51 @@ Amend URLconf First, open the ``polls/urls.py`` URLconf and change it like so:: from django.conf.urls import patterns, url from django.views.generic import DetailView, ListView from polls.models import Poll from polls import views urlpatterns = patterns('', url(r'^$', ListView.as_view( queryset=Poll.objects.order_by('-pub_date')[:5], context_object_name='latest_poll_list', template_name='polls/index.html'), name='index'), url(r'^(?P<pk>\d+)/$', DetailView.as_view( model=Poll, template_name='polls/detail.html'), name='detail'), url(r'^(?P<pk>\d+)/results/$', DetailView.as_view( model=Poll, template_name='polls/results.html'), name='results'), url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote', name='vote'), url(r'^$', views.IndexView.as_view(), name='index'), url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'), url(r'^(?P<pk>\d+)/results/$', views.ResultsView.as_view(), name='results'), url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'), ) Amend views ----------- Next, we're going to remove our old ``index``, ``detail``, and ``results`` views and use Django's generic views instead. To do so, open the ``polls/views.py`` file and change it like so:: 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 polls.models import Choice, Poll class IndexView(generic.ListView): template_name = 'polls/index.html' context_object_name = 'latest_poll_list' def get_queryset(self): """Return the last five published polls.""" return Poll.objects.order_by('-pub_date')[:5] class DetailView(generic.DetailView): model = Poll template_name = 'polls/detail.html' class ResultsView(generic.DetailView): model = Poll template_name = 'polls/results.html' def vote(request, poll_id): .... We're using two generic views here: :class:`~django.views.generic.list.ListView` and :class:`~django.views.generic.detail.DetailView`. Respectively, those Loading @@ -238,7 +257,7 @@ two views abstract the concepts of "display a list of objects" and "display a detail page for a particular type of object." * Each generic view needs to know what model it will be acting upon. This is provided using the ``model`` parameter. upon. This is provided using the ``model`` attribute. * The :class:`~django.views.generic.detail.DetailView` generic view expects the primary key value captured from the URL to be called Loading @@ -248,7 +267,7 @@ two views abstract the concepts of "display a list of objects" and By default, the :class:`~django.views.generic.detail.DetailView` generic view uses a template called ``<app name>/<model name>_detail.html``. In our case, it'll use the template ``"polls/poll_detail.html"``. The ``template_name`` argument is used to tell Django to use a specific ``template_name`` attribute is used to tell Django to use a specific template name instead of the autogenerated default template name. We also specify the ``template_name`` for the ``results`` list view -- this ensures that the results view and the detail view have a Loading @@ -268,16 +287,11 @@ automatically -- since we're using a Django model (``Poll``), Django is able to determine an appropriate name for the context variable. However, for ListView, the automatically generated context variable is ``poll_list``. To override this we provide the ``context_object_name`` option, specifying that we want to use ``latest_poll_list`` instead. attribute, specifying that we want to use ``latest_poll_list`` instead. As an alternative approach, you could change your templates to match the new default context variables -- but it's a lot easier to just tell Django to use the variable you want. You can now delete the ``index()``, ``detail()`` and ``results()`` views from ``polls/views.py``. We don't need them anymore -- they have been replaced by generic views. You can also delete the import for ``HttpResponse``, which is no longer required. Run the server, and use your new polling app based on generic views. For full details on generic views, see the :doc:`generic views documentation Loading docs/intro/tutorial05.txt +35 −42 Original line number Diff line number Diff line Loading @@ -378,45 +378,40 @@ Improving our view The list of polls shows polls that aren't published yet (i.e. those that have a ``pub_date`` in the future). Let's fix that. In :doc:`Tutorial 4 </intro/tutorial04>` we deleted the view functions from ``views.py`` in favor of a :class:`~django.views.generic.list.ListView` in ``urls.py``:: In :doc:`Tutorial 4 </intro/tutorial04>` we introduced a class-based view, based on :class:`~django.views.generic.list.ListView`:: url(r'^$', ListView.as_view( queryset=Poll.objects.order_by('-pub_date')[:5], context_object_name='latest_poll_list', template_name='polls/index.html'), name='index'), class IndexView(generic.ListView): template_name = 'polls/index.html' context_object_name = 'latest_poll_list' def get_queryset(self): """Return the last five published polls.""" return Poll.objects.order_by('-pub_date')[:5] ``response.context_data['latest_poll_list']`` extracts the data this view places into the context. We need to amend the line that gives us the ``queryset``:: queryset=Poll.objects.order_by('-pub_date')[:5], Let's change the queryset so that it also checks the date by comparing it with ``timezone.now()``. First we need to add an import:: We need to amend the ``get_queryset`` method and change it so that it also checks the date by comparing it with ``timezone.now()``. First we need to add an import:: from django.utils import timezone and then we must amend the existing ``url`` function to:: and then we must amend the ``get_queryset`` method like so:: url(r'^$', ListView.as_view( queryset=Poll.objects.filter(pub_date__lte=timezone.now) \ .order_by('-pub_date')[:5], context_object_name='latest_poll_list', template_name='polls/index.html'), name='index'), def get_queryset(self): """ Return the last five published polls (not including those set to be published in the future). """ return Poll.objects.filter( pub_date__lte=timezone.now() ).order_by('-pub_date')[:5] ``Poll.objects.filter(pub_date__lte=timezone.now)`` returns a queryset ``Poll.objects.filter(pub_date__lte=timezone.now())`` returns a queryset containing Polls whose ``pub_date`` is less than or equal to - that is, earlier than or equal to - ``timezone.now``. Notice that we use a callable queryset argument, ``timezone.now``, which will be evaluated at request time. If we had included the parentheses, ``timezone.now()`` would be evaluated just once when the web server is started. than or equal to - ``timezone.now``. Testing our new view -------------------- Loading Loading @@ -527,20 +522,18 @@ Testing the ``DetailView`` What we have works well; however, even though future polls don't appear in the *index*, users can still reach them if they know or guess the right URL. So we need similar constraints in the ``DetailViews``, by adding:: queryset=Poll.objects.filter(pub_date__lte=timezone.now) need to add a similar constraint to ``DetailView``:: to them - for example:: url(r'^(?P<pk>\d+)/$', DetailView.as_view( queryset=Poll.objects.filter(pub_date__lte=timezone.now), model=Poll, template_name='polls/detail.html'), name='detail'), class DetailView(generic.DetailView): ... def get_queryset(self): """ Excludes any polls that aren't published yet. """ return Poll.objects.filter(pub_date__lte=timezone.now()) and of course, we will add some tests, to check that a ``Poll`` whose And of course, we will add some tests, to check that a ``Poll`` whose ``pub_date`` is in the past can be displayed, and that one with a ``pub_date`` in the future is not:: Loading @@ -566,9 +559,9 @@ in the future is not:: Ideas for more tests -------------------- We ought to add similar ``queryset`` arguments to the other ``DetailView`` URLs, and create a new test class for each view. They'll be very similar to what we have just created; in fact there will be a lot of repetition. We ought to add a similar ``get_queryset`` method to ``ResultsView`` and create a new test class for that view. It'll be very similar to what we have just created; in fact there will be a lot of repetition. We could also improve our application in other ways, adding tests along the way. For example, it's silly that ``Polls`` can be published on the site that Loading Loading
docs/intro/tutorial04.txt +42 −28 Original line number Diff line number Diff line Loading @@ -185,7 +185,7 @@ conversion. We will: 2. Delete some of the old, unneeded views. 3. Fix up URL handling for the new views. 3. Introduce new views based on Django's generic views. Read on for details. Loading @@ -205,32 +205,51 @@ Amend URLconf First, open the ``polls/urls.py`` URLconf and change it like so:: from django.conf.urls import patterns, url from django.views.generic import DetailView, ListView from polls.models import Poll from polls import views urlpatterns = patterns('', url(r'^$', ListView.as_view( queryset=Poll.objects.order_by('-pub_date')[:5], context_object_name='latest_poll_list', template_name='polls/index.html'), name='index'), url(r'^(?P<pk>\d+)/$', DetailView.as_view( model=Poll, template_name='polls/detail.html'), name='detail'), url(r'^(?P<pk>\d+)/results/$', DetailView.as_view( model=Poll, template_name='polls/results.html'), name='results'), url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote', name='vote'), url(r'^$', views.IndexView.as_view(), name='index'), url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'), url(r'^(?P<pk>\d+)/results/$', views.ResultsView.as_view(), name='results'), url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'), ) Amend views ----------- Next, we're going to remove our old ``index``, ``detail``, and ``results`` views and use Django's generic views instead. To do so, open the ``polls/views.py`` file and change it like so:: 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 polls.models import Choice, Poll class IndexView(generic.ListView): template_name = 'polls/index.html' context_object_name = 'latest_poll_list' def get_queryset(self): """Return the last five published polls.""" return Poll.objects.order_by('-pub_date')[:5] class DetailView(generic.DetailView): model = Poll template_name = 'polls/detail.html' class ResultsView(generic.DetailView): model = Poll template_name = 'polls/results.html' def vote(request, poll_id): .... We're using two generic views here: :class:`~django.views.generic.list.ListView` and :class:`~django.views.generic.detail.DetailView`. Respectively, those Loading @@ -238,7 +257,7 @@ two views abstract the concepts of "display a list of objects" and "display a detail page for a particular type of object." * Each generic view needs to know what model it will be acting upon. This is provided using the ``model`` parameter. upon. This is provided using the ``model`` attribute. * The :class:`~django.views.generic.detail.DetailView` generic view expects the primary key value captured from the URL to be called Loading @@ -248,7 +267,7 @@ two views abstract the concepts of "display a list of objects" and By default, the :class:`~django.views.generic.detail.DetailView` generic view uses a template called ``<app name>/<model name>_detail.html``. In our case, it'll use the template ``"polls/poll_detail.html"``. The ``template_name`` argument is used to tell Django to use a specific ``template_name`` attribute is used to tell Django to use a specific template name instead of the autogenerated default template name. We also specify the ``template_name`` for the ``results`` list view -- this ensures that the results view and the detail view have a Loading @@ -268,16 +287,11 @@ automatically -- since we're using a Django model (``Poll``), Django is able to determine an appropriate name for the context variable. However, for ListView, the automatically generated context variable is ``poll_list``. To override this we provide the ``context_object_name`` option, specifying that we want to use ``latest_poll_list`` instead. attribute, specifying that we want to use ``latest_poll_list`` instead. As an alternative approach, you could change your templates to match the new default context variables -- but it's a lot easier to just tell Django to use the variable you want. You can now delete the ``index()``, ``detail()`` and ``results()`` views from ``polls/views.py``. We don't need them anymore -- they have been replaced by generic views. You can also delete the import for ``HttpResponse``, which is no longer required. Run the server, and use your new polling app based on generic views. For full details on generic views, see the :doc:`generic views documentation Loading
docs/intro/tutorial05.txt +35 −42 Original line number Diff line number Diff line Loading @@ -378,45 +378,40 @@ Improving our view The list of polls shows polls that aren't published yet (i.e. those that have a ``pub_date`` in the future). Let's fix that. In :doc:`Tutorial 4 </intro/tutorial04>` we deleted the view functions from ``views.py`` in favor of a :class:`~django.views.generic.list.ListView` in ``urls.py``:: In :doc:`Tutorial 4 </intro/tutorial04>` we introduced a class-based view, based on :class:`~django.views.generic.list.ListView`:: url(r'^$', ListView.as_view( queryset=Poll.objects.order_by('-pub_date')[:5], context_object_name='latest_poll_list', template_name='polls/index.html'), name='index'), class IndexView(generic.ListView): template_name = 'polls/index.html' context_object_name = 'latest_poll_list' def get_queryset(self): """Return the last five published polls.""" return Poll.objects.order_by('-pub_date')[:5] ``response.context_data['latest_poll_list']`` extracts the data this view places into the context. We need to amend the line that gives us the ``queryset``:: queryset=Poll.objects.order_by('-pub_date')[:5], Let's change the queryset so that it also checks the date by comparing it with ``timezone.now()``. First we need to add an import:: We need to amend the ``get_queryset`` method and change it so that it also checks the date by comparing it with ``timezone.now()``. First we need to add an import:: from django.utils import timezone and then we must amend the existing ``url`` function to:: and then we must amend the ``get_queryset`` method like so:: url(r'^$', ListView.as_view( queryset=Poll.objects.filter(pub_date__lte=timezone.now) \ .order_by('-pub_date')[:5], context_object_name='latest_poll_list', template_name='polls/index.html'), name='index'), def get_queryset(self): """ Return the last five published polls (not including those set to be published in the future). """ return Poll.objects.filter( pub_date__lte=timezone.now() ).order_by('-pub_date')[:5] ``Poll.objects.filter(pub_date__lte=timezone.now)`` returns a queryset ``Poll.objects.filter(pub_date__lte=timezone.now())`` returns a queryset containing Polls whose ``pub_date`` is less than or equal to - that is, earlier than or equal to - ``timezone.now``. Notice that we use a callable queryset argument, ``timezone.now``, which will be evaluated at request time. If we had included the parentheses, ``timezone.now()`` would be evaluated just once when the web server is started. than or equal to - ``timezone.now``. Testing our new view -------------------- Loading Loading @@ -527,20 +522,18 @@ Testing the ``DetailView`` What we have works well; however, even though future polls don't appear in the *index*, users can still reach them if they know or guess the right URL. So we need similar constraints in the ``DetailViews``, by adding:: queryset=Poll.objects.filter(pub_date__lte=timezone.now) need to add a similar constraint to ``DetailView``:: to them - for example:: url(r'^(?P<pk>\d+)/$', DetailView.as_view( queryset=Poll.objects.filter(pub_date__lte=timezone.now), model=Poll, template_name='polls/detail.html'), name='detail'), class DetailView(generic.DetailView): ... def get_queryset(self): """ Excludes any polls that aren't published yet. """ return Poll.objects.filter(pub_date__lte=timezone.now()) and of course, we will add some tests, to check that a ``Poll`` whose And of course, we will add some tests, to check that a ``Poll`` whose ``pub_date`` is in the past can be displayed, and that one with a ``pub_date`` in the future is not:: Loading @@ -566,9 +559,9 @@ in the future is not:: Ideas for more tests -------------------- We ought to add similar ``queryset`` arguments to the other ``DetailView`` URLs, and create a new test class for each view. They'll be very similar to what we have just created; in fact there will be a lot of repetition. We ought to add a similar ``get_queryset`` method to ``ResultsView`` and create a new test class for that view. It'll be very similar to what we have just created; in fact there will be a lot of repetition. We could also improve our application in other ways, adding tests along the way. For example, it's silly that ``Polls`` can be published on the site that Loading