Fixed #14878 -- Clarified the way verbose_name_plural is used in generic list views as a context variable. Thanks to diegueus9 for the report.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15133 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2011-01-03 13:15:58 +00:00
parent 2a5105ac15
commit a00e8d4e42
10 changed files with 61 additions and 13 deletions

View File

@ -2,6 +2,7 @@ import re
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.http import Http404 from django.http import Http404
from django.utils.encoding import smart_str
from django.views.generic.base import TemplateResponseMixin, View from django.views.generic.base import TemplateResponseMixin, View
@ -79,8 +80,8 @@ class SingleObjectMixin(object):
if self.context_object_name: if self.context_object_name:
return self.context_object_name return self.context_object_name
elif hasattr(obj, '_meta'): elif hasattr(obj, '_meta'):
return re.sub('[^a-zA-Z0-9]+', '_', return smart_str(re.sub('[^a-zA-Z0-9]+', '_',
obj._meta.verbose_name.lower()) obj._meta.verbose_name.lower()))
else: else:
return None return None

View File

@ -1,9 +1,12 @@
import re
from django.core.paginator import Paginator, InvalidPage from django.core.paginator import Paginator, InvalidPage
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.http import Http404 from django.http import Http404
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from django.views.generic.base import TemplateResponseMixin, View from django.views.generic.base import TemplateResponseMixin, View
class MultipleObjectMixin(object): class MultipleObjectMixin(object):
allow_empty = True allow_empty = True
queryset = None queryset = None
@ -76,7 +79,8 @@ class MultipleObjectMixin(object):
if self.context_object_name: if self.context_object_name:
return self.context_object_name return self.context_object_name
elif hasattr(object_list, 'model'): elif hasattr(object_list, 'model'):
return smart_str(object_list.model._meta.verbose_name_plural) return smart_str(re.sub('[^a-zA-Z0-9]+', '_',
object_list.model._meta.verbose_name_plural.lower()))
else: else:
return None return None

View File

@ -428,7 +428,7 @@ FormMixin
.. method:: get_form_kwargs() .. method:: get_form_kwargs()
Build the keyword arguments requried to instanciate an the form. Build the keyword arguments requried to instanciate an the form.
The ``initial`` argument is set to :meth:`.get_initial`. If the The ``initial`` argument is set to :meth:`.get_initial`. If the
request is a ``POST`` or ``PUT``, the request data (``request.POST`` request is a ``POST`` or ``PUT``, the request data (``request.POST``
and ``request.FILES``) will also be provided. and ``request.FILES``) will also be provided.

View File

@ -206,14 +206,23 @@ their attributes or methods.
Making "friendly" template contexts Making "friendly" template contexts
----------------------------------- -----------------------------------
You might have noticed that our sample publisher list template stores all the You might have noticed that our sample publisher list template stores
publishers in a variable named ``object_list``. While this works just fine, it all the publishers in a variable named ``object_list``. While this
isn't all that "friendly" to template authors: they have to "just know" that works just fine, it isn't all that "friendly" to template authors:
they're dealing with publishers here. A more obvious name for that variable they have to "just know" that they're dealing with publishers here.
would be ``publisher_list``.
We can change the name of that variable easily with the ``context_object_name`` Well, if you're dealing with a Django object, this is already done for
attribute - here, we'll override it in the URLconf, since it's a simple change: you. When you are dealing with an object or queryset, Django is able
to populate the context using the verbose name (or the plural verbose
name, in the case of a list of objects) of the object being displayed.
This is provided in addition to the default ``object_list`` entry, but
contains exactly the same data.
If the verbose name (or plural verbose name) still isn't a good match,
you can manually set the name of the context variable. The
``context_object_name`` attribute on a generic view specifies the
context variable to use. In this example, we'll override it in the
URLconf, since it's a simple change:
.. parsed-literal:: .. parsed-literal::

View File

@ -1,7 +1,7 @@
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase from django.test import TestCase
from regressiontests.generic_views.models import Author, Page from regressiontests.generic_views.models import Artist, Author, Page
class DetailViewTest(TestCase): class DetailViewTest(TestCase):
@ -28,6 +28,13 @@ class DetailViewTest(TestCase):
self.assertEqual(res.context['author'], Author.objects.get(slug='scott-rosenberg')) self.assertEqual(res.context['author'], Author.objects.get(slug='scott-rosenberg'))
self.assertTemplateUsed(res, 'generic_views/author_detail.html') self.assertTemplateUsed(res, 'generic_views/author_detail.html')
def test_verbose_name(self):
res = self.client.get('/detail/artist/1/')
self.assertEqual(res.status_code, 200)
self.assertEqual(res.context['object'], Artist.objects.get(pk=1))
self.assertEqual(res.context['professional_artist'], Artist.objects.get(pk=1))
self.assertTemplateUsed(res, 'generic_views/artist_detail.html')
def test_template_name(self): def test_template_name(self):
res = self.client.get('/detail/author/1/template_name/') res = self.client.get('/detail/author/1/template_name/')
self.assertEqual(res.status_code, 200) self.assertEqual(res.status_code, 200)

View File

@ -1,4 +1,11 @@
[ [
{
"model": "generic_views.artist",
"pk": 1,
"fields": {
"name": "Rene Magritte"
}
},
{ {
"model": "generic_views.author", "model": "generic_views.author",
"pk": 1, "pk": 1,

View File

@ -1,7 +1,7 @@
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase from django.test import TestCase
from regressiontests.generic_views.models import Author from regressiontests.generic_views.models import Author, Artist
from regressiontests.generic_views.views import CustomPaginator from regressiontests.generic_views.views import CustomPaginator
class ListViewTests(TestCase): class ListViewTests(TestCase):
@ -106,6 +106,16 @@ class ListViewTests(TestCase):
self.assertEqual(res.status_code, 200) self.assertEqual(res.status_code, 200)
self.assertEqual(len(res.context['object_list']), 1) self.assertEqual(len(res.context['object_list']), 1)
def test_verbose_name(self):
res = self.client.get('/list/artists/')
self.assertEqual(res.status_code, 200)
self.assertTemplateUsed(res, 'generic_views/list.html')
self.assertEqual(list(res.context['object_list']), list(Artist.objects.all()))
self.assertIs(res.context['professional_artists'], res.context['object_list'])
self.assertIsNone(res.context['paginator'])
self.assertIsNone(res.context['page_obj'])
self.assertFalse(res.context['is_paginated'])
def test_allow_empty_false(self): def test_allow_empty_false(self):
res = self.client.get('/list/authors/notempty/') res = self.client.get('/list/authors/notempty/')
self.assertEqual(res.status_code, 200) self.assertEqual(res.status_code, 200)

View File

@ -5,6 +5,8 @@ class Artist(models.Model):
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
verbose_name = 'professional artist'
verbose_name_plural = 'professional artists'
def __unicode__(self): def __unicode__(self):
return self.name return self.name

View File

@ -100,6 +100,9 @@ urlpatterns = patterns('',
views.DictList.as_view()), views.DictList.as_view()),
(r'^list/dict/paginated/$', (r'^list/dict/paginated/$',
views.DictList.as_view(paginate_by=1)), views.DictList.as_view(paginate_by=1)),
url(r'^list/artists/$',
views.ArtistList.as_view(),
name="artists_list"),
url(r'^list/authors/$', url(r'^list/authors/$',
views.AuthorList.as_view(), views.AuthorList.as_view(),
name="authors_list"), name="authors_list"),

View File

@ -47,6 +47,11 @@ class DictList(generic.ListView):
template_name = 'generic_views/list.html' template_name = 'generic_views/list.html'
class ArtistList(generic.ListView):
template_name = 'generic_views/list.html'
queryset = Artist.objects.all()
class AuthorList(generic.ListView): class AuthorList(generic.ListView):
queryset = Author.objects.all() queryset = Author.objects.all()