diff --git a/django/contrib/admin/filters.py b/django/contrib/admin/filters.py
index b2d74ce..550683b 100644
a
|
b
|
from django.contrib.admin.util import (get_model_from_relation,
|
17 | 17 | |
18 | 18 | class ListFilter(object): |
19 | 19 | title = None # Human-readable title to appear in the right sidebar. |
| 20 | template = 'admin/filter.html' |
20 | 21 | |
21 | 22 | def __init__(self, request, params, model, model_admin): |
22 | 23 | # This dictionary will eventually contain the request's query string |
diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
index ba3d132..067553b 100644
a
|
b
|
from django.utils.text import capfirst
|
13 | 13 | from django.utils.translation import ugettext as _ |
14 | 14 | from django.utils.encoding import smart_unicode, force_unicode |
15 | 15 | from django.template import Library |
16 | | |
| 16 | from django.template.loader import get_template |
| 17 | from django.template.context import Context |
17 | 18 | |
18 | 19 | register = Library() |
19 | 20 | |
… |
… |
def search_form(cl):
|
360 | 361 | 'search_var': SEARCH_VAR |
361 | 362 | } |
362 | 363 | |
363 | | @register.inclusion_tag('admin/filter.html') |
| 364 | @register.simple_tag() |
364 | 365 | def admin_list_filter(cl, spec): |
365 | | return {'title': spec.title, 'choices' : list(spec.choices(cl))} |
| 366 | t = get_template(spec.template) |
| 367 | ctx = Context({'title': spec.title, 'choices' : list(spec.choices(cl)), 'spec': spec}) |
| 368 | return t.render(ctx) |
366 | 369 | |
367 | 370 | @register.inclusion_tag('admin/actions.html', takes_context=True) |
368 | 371 | def admin_actions(context): |
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
index 76ea780..c40ce89 100644
a
|
b
|
subclass::
|
697 | 697 | |
698 | 698 | .. note:: |
699 | 699 | |
700 | | The ``FieldListFilter`` API is currently considered internal |
701 | | and prone to refactoring. |
| 700 | The ``FieldListFilter`` API is currently considered internal and prone |
| 701 | to refactoring. |
| 702 | |
| 703 | .. versionadded:: 1.4 |
| 704 | |
| 705 | It is possible to specify a custom template for rendering a list filter:: |
| 706 | |
| 707 | class FilterWithCustomTemplate(SimpleListFilter): |
| 708 | template = "custom_template.html" |
| 709 | |
| 710 | See the default template provided by django (``admin/filter.html``) for |
| 711 | a concrete example. |
702 | 712 | |
703 | 713 | .. attribute:: ModelAdmin.list_max_show_all |
704 | 714 | |
diff --git a/tests/regressiontests/admin_views/admin.py b/tests/regressiontests/admin_views/admin.py
index 8c5b511..4582938 100644
a
|
b
|
from django.conf.urls import patterns, url
|
13 | 13 | from django.db import models |
14 | 14 | from django.forms.models import BaseModelFormSet |
15 | 15 | from django.http import HttpResponse |
| 16 | from django.contrib.admin import BooleanFieldListFilter |
16 | 17 | |
17 | 18 | from .models import (Article, Chapter, Account, Media, Child, Parent, Picture, |
18 | 19 | Widget, DooHickey, Grommet, Whatsit, FancyDoodad, Category, Link, |
… |
… |
from .models import (Article, Chapter, Account, Media, Child, Parent, Picture,
|
25 | 26 | CoverLetter, Story, OtherStory, Book, Promo, ChapterXtra1, Pizza, Topping, |
26 | 27 | Album, Question, Answer, ComplexSortedPerson, PrePopulatedPostLargeSlug, |
27 | 28 | AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod, |
28 | | AdminOrderedCallable, Report) |
| 29 | AdminOrderedCallable, Report, Color2) |
29 | 30 | |
30 | 31 | |
31 | 32 | def callable_year(dt_value): |
… |
… |
class ReportAdmin(admin.ModelAdmin):
|
512 | 513 | name='cable_extra'), |
513 | 514 | ) |
514 | 515 | |
| 516 | class CustomTemplateBooleanFieldListFilter(BooleanFieldListFilter): |
| 517 | template = 'custom_filter_template.html' |
| 518 | |
| 519 | class CustomTemplateFilterColorAdmin(admin.ModelAdmin): |
| 520 | list_filter = (('warm', CustomTemplateBooleanFieldListFilter),) |
| 521 | |
515 | 522 | site = admin.AdminSite(name="admin") |
516 | 523 | site.register(Article, ArticleAdmin) |
517 | 524 | site.register(CustomArticle, CustomArticleAdmin) |
… |
… |
site.register(AdminOrderedField, AdminOrderedFieldAdmin)
|
581 | 588 | site.register(AdminOrderedModelMethod, AdminOrderedModelMethodAdmin) |
582 | 589 | site.register(AdminOrderedAdminMethod, AdminOrderedAdminMethodAdmin) |
583 | 590 | site.register(AdminOrderedCallable, AdminOrderedCallableAdmin) |
| 591 | site.register(Color2, CustomTemplateFilterColorAdmin) |
584 | 592 | |
585 | 593 | # Register core models we need in our tests |
586 | 594 | from django.contrib.auth.models import User, Group |
diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py
index 0029fcc..0e0fa81 100644
a
|
b
|
class Color(models.Model):
|
106 | 106 | def __unicode__(self): |
107 | 107 | return self.value |
108 | 108 | |
| 109 | # we replicate Color to register with another ModelAdmin |
| 110 | class Color2(Color): |
| 111 | class Meta: |
| 112 | proxy = True |
109 | 113 | |
110 | 114 | class Thing(models.Model): |
111 | 115 | title = models.CharField(max_length=20) |
diff --git a/tests/regressiontests/admin_views/templates/custom_filter_template.html b/tests/regressiontests/admin_views/templates/custom_filter_template.html
new file mode 100644
index 0000000..e5c9a8e
-
|
+
|
|
| 1 | <h3>By {{ filter_title }} (custom)</h3> |
| 2 | <ul> |
| 3 | {% for choice in choices %} |
| 4 | <li{% if choice.selected %} class="selected"{% endif %}> |
| 5 | <a href="{{ choice.query_string|iriencode }}">{{ choice.display }}</a></li> |
| 6 | {% endfor %} |
| 7 | </ul> |
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
index b0120ce..b05683a 100644
a
|
b
|
|
1 | 1 | # coding: utf-8 |
2 | 2 | from __future__ import with_statement, absolute_import |
3 | 3 | |
| 4 | import os |
4 | 5 | import re |
5 | 6 | import datetime |
6 | 7 | import urlparse |
… |
… |
class AdminViewBasicTest(TestCase):
|
573 | 574 | except SuspiciousOperation: |
574 | 575 | self.fail("Filters should be allowed if they are defined on a ForeignKey pointing to this model") |
575 | 576 | |
| 577 | def test_filter_with_custom_template(self): |
| 578 | """ |
| 579 | Ensure that one can use a custom template to render an admin filter. |
| 580 | Refs #17515. |
| 581 | """ |
| 582 | template_dirs = settings.TEMPLATE_DIRS + ( |
| 583 | os.path.join(os.path.dirname(__file__), 'templates'),) |
| 584 | with self.settings(TEMPLATE_DIRS=template_dirs): |
| 585 | response = self.client.get("/test_admin/admin/admin_views/color2/") |
| 586 | self.assertTrue('custom_filter_template.html' in [t.name for t in response.templates]) |
| 587 | |
| 588 | |
576 | 589 | class AdminJavaScriptTest(AdminViewBasicTest): |
577 | 590 | urls = "regressiontests.admin_views.urls" |
578 | 591 | |