mirror of
				https://github.com/django/django.git
				synced 2025-11-03 21:25:09 +00:00 
			
		
		
		
	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
					
				
					 10 changed files with 61 additions and 13 deletions
				
			
		| 
						 | 
					@ -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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue