diff --git a/django/views/generic/detail.py b/django/views/generic/detail.py
index 7e9047f..501f0ef 100644
a
|
b
|
class SingleObjectMixin(object):
|
15 | 15 | queryset = None |
16 | 16 | slug_field = 'slug' |
17 | 17 | context_object_name = None |
| 18 | slug_url_kwarg = 'slug' |
| 19 | pk_url_kwarg = 'pk' |
18 | 20 | |
19 | 21 | def get_object(self, queryset=None): |
20 | 22 | """ |
… |
… |
class SingleObjectMixin(object):
|
29 | 31 | queryset = self.get_queryset() |
30 | 32 | |
31 | 33 | # Next, try looking up by primary key. |
32 | | pk = self.kwargs.get('pk', None) |
33 | | slug = self.kwargs.get('slug', None) |
| 34 | pk = self.kwargs.get(self.pk_url_kwarg, None) |
| 35 | slug = self.kwargs.get(self.slug_url_kwarg, None) |
34 | 36 | if pk is not None: |
35 | 37 | queryset = queryset.filter(pk=pk) |
36 | 38 | |
diff --git a/docs/ref/class-based-views.txt b/docs/ref/class-based-views.txt
index e9f3152..132913f 100644
a
|
b
|
SingleObjectMixin
|
136 | 136 | The name of the field on the model that contains the slug. By default, |
137 | 137 | ``slug_field`` is ``'slug'``. |
138 | 138 | |
| 139 | .. attribute:: slug_url_kwarg |
| 140 | |
| 141 | The name of the URLConf keyword argument that contains the slug. By |
| 142 | default, ``slug_url_kwarg`` is ``'slug'``. |
| 143 | |
| 144 | .. attribute:: pk_url_kwarg |
| 145 | |
| 146 | The name of the URLConf keyword argument that contains the primary key. |
| 147 | By default, ``pk_url_kwarg`` is ``'pk'``. |
| 148 | |
139 | 149 | .. attribute:: context_object_name |
140 | 150 | |
141 | 151 | Designates the name of the variable to use in the context. |
… |
… |
SingleObjectMixin
|
146 | 156 | ``queryset`` is provided, that queryset will be used as the |
147 | 157 | source of objects; otherwise, |
148 | 158 | :meth:`~SingleObjectMixin.get_queryset` will be used. |
149 | | :meth:`~SingleObjectMixin.get_object` looks for a ``pk`` |
150 | | argument in the arguments to the view; if ``pk`` is found, |
151 | | this method performs a primary-key based lookup using that |
152 | | value. If no ``pk`` argument is found, it looks for a ``slug`` |
| 159 | ``get_object()`` looks for a |
| 160 | :attr:`SingleObjectMixin.pk_url_kwarg` argument in the arguments |
| 161 | to the view; if this argument is found, this method performs a |
| 162 | primary-key based lookup using that value. If this argument is not |
| 163 | found, it looks for a :attr:`SingleObjectMixin.slug_url_kwarg` |
153 | 164 | argument, and performs a slug lookup using the |
154 | 165 | :attr:`SingleObjectMixin.slug_field`. |
155 | 166 | |
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
index 7fdf0d7..d7843ba 100644
a
|
b
|
A new helper function,
|
78 | 78 | ``template.Library`` to ease the creation of template tags that store some |
79 | 79 | data in a specified context variable. |
80 | 80 | |
| 81 | Customizable ``SingleObjectMixin`` URLConf kwargs |
| 82 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 83 | |
| 84 | Two new attributes, |
| 85 | :attr:`pk_url_kwarg<django.views.generic.detail.SingleObjectMixin.pk_url_kwarg>` and |
| 86 | :attr:`slug_url_kwarg<django.views.generic.detail.SingleObjectMixin.slug_url_kwarg>`, |
| 87 | have been added to :class:`django.views.generic.detail.SingleObjectMixin` to |
| 88 | enable the customization of URLConf keyword arguments used for single |
| 89 | object generic views. |
| 90 | |
81 | 91 | CSRF improvements |
82 | 92 | ~~~~~~~~~~~~~~~~~ |
83 | 93 | |
diff --git a/tests/regressiontests/generic_views/detail.py b/tests/regressiontests/generic_views/detail.py
index e8f2050..cd55d26 100644
a
|
b
|
class DetailViewTest(TestCase):
|
21 | 21 | self.assertEqual(res.context['author'], Author.objects.get(pk=1)) |
22 | 22 | self.assertTemplateUsed(res, 'generic_views/author_detail.html') |
23 | 23 | |
| 24 | def test_detail_by_custom_pk(self): |
| 25 | res = self.client.get('/detail/author/bycustompk/1/') |
| 26 | self.assertEqual(res.status_code, 200) |
| 27 | self.assertEqual(res.context['object'], Author.objects.get(pk=1)) |
| 28 | self.assertEqual(res.context['author'], Author.objects.get(pk=1)) |
| 29 | self.assertTemplateUsed(res, 'generic_views/author_detail.html') |
| 30 | |
24 | 31 | def test_detail_by_slug(self): |
25 | 32 | res = self.client.get('/detail/author/byslug/scott-rosenberg/') |
26 | 33 | self.assertEqual(res.status_code, 200) |
27 | 34 | self.assertEqual(res.context['object'], Author.objects.get(slug='scott-rosenberg')) |
28 | 35 | self.assertEqual(res.context['author'], Author.objects.get(slug='scott-rosenberg')) |
29 | 36 | self.assertTemplateUsed(res, 'generic_views/author_detail.html') |
| 37 | |
| 38 | def test_detail_by_custom_slug(self): |
| 39 | res = self.client.get('/detail/author/bycustomslug/scott-rosenberg/') |
| 40 | self.assertEqual(res.status_code, 200) |
| 41 | self.assertEqual(res.context['object'], Author.objects.get(slug='scott-rosenberg')) |
| 42 | self.assertEqual(res.context['author'], Author.objects.get(slug='scott-rosenberg')) |
| 43 | self.assertTemplateUsed(res, 'generic_views/author_detail.html') |
30 | 44 | |
31 | 45 | def test_verbose_name(self): |
32 | 46 | res = self.client.get('/detail/artist/1/') |
diff --git a/tests/regressiontests/generic_views/urls.py b/tests/regressiontests/generic_views/urls.py
index 067c1f6..dc86e4d 100644
a
|
b
|
urlpatterns = patterns('',
|
30 | 30 | url(r'^detail/author/(?P<pk>\d+)/$', |
31 | 31 | views.AuthorDetail.as_view(), |
32 | 32 | name="author_detail"), |
| 33 | (r'^detail/author/bycustompk/(?P<foo>\d+)/$', |
| 34 | views.AuthorDetail.as_view(pk_url_kwarg='foo')), |
33 | 35 | (r'^detail/author/byslug/(?P<slug>[\w-]+)/$', |
34 | 36 | views.AuthorDetail.as_view()), |
| 37 | (r'^detail/author/bycustomslug/(?P<foo>[\w-]+)/$', |
| 38 | views.AuthorDetail.as_view(slug_url_kwarg='foo')), |
35 | 39 | (r'^detail/author/(?P<pk>\d+)/template_name_suffix/$', |
36 | 40 | views.AuthorDetail.as_view(template_name_suffix='_view')), |
37 | 41 | (r'^detail/author/(?P<pk>\d+)/template_name/$', |