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:
parent
2a5105ac15
commit
a00e8d4e42
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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::
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue