Reusing Django’s filter_horizontal
Django’s admin site documentation describes ModelAdmin’s filter_horizontal option as a “nifty unobtrusive JavaScript” to use in place of “the usability-challenged <select multiple>”. HTML’s multiple select does indeed suck for any number of options that require scrolling. Inevitably, when editing an existing entry, you or your users will eventually erase an existing option without knowing it.
Django’s horizontal and vertical filter solutions change these select boxes into one box of unselected options and one box of selected options, making the selected options much more obvious, and making it pretty much impossible to accidentally remove an existing selection.
You can use this JavaScript in your own forms. It consists of several JavaScript files, one CSS file, and a snippet of HTML right next to the pop-up button.
JavaScript and CSS
Assuming that you’ve made use of Django’s popup add form, you already have RelatedObjectLookups.js on your template somewhere. Add several more JavaScript files as well as one CSS file from Django’s built-in library:
- <script type="text/javascript" src="/media/js/admin/RelatedObjectLookups.js"></script>
- <script type="text/javascript" src="/admin/jsi18n/"></script>
- <script type="text/javascript" src="/media/js/core.js"></script>
- <script type="text/javascript" src="/media/js/SelectBox.js"></script>
- <script type="text/javascript" src="/media/js/SelectFilter2.js"></script>
- <link rel="stylesheet" type="text/css" href="/media/css/widgets.css" />
Call the JavaScript
If you’re using the admin-form pop-ups as I described earlier in Replicating Django’s admin form pop-ups, you have a template snippet called “form/popupplus.html”. This template is called by both SelectWithPop and MultipleSelectWithPop. Only MultipleSelectWithPop needs filter_horizontal, so add a flag to that class’s render method’s context:
[toggle code]
-
class MultipleSelectWithPop(forms.SelectMultiple):
-
def render(self, name, *args, **kwargs):
- html = super(MultipleSelectWithPop, self).render(name, *args, **kwargs)
- popupplus = render_to_string("form/popupplus.html", {'field': name, 'multiple': True})
- return html+popupplus
-
def render(self, name, *args, **kwargs):
And then, inside of popupplus.html, call the SelectFilter JavaScript:
[toggle code]
-
{% if multiple %}
-
<script type="text/javascript">
-
addEvent(window, "load", function(e) {
- SelectFilter.init("id_{{ field }}", "{{ field }}", 0, "/media/");
- });
-
addEvent(window, "load", function(e) {
- </script>
-
<script type="text/javascript">
- {% endif %}
The first parameter to SelectFilter is the field’s ID, the second is the field’s name, the third is 0 for horizontal or 1 for vertical (stacked), and the fourth is the admin media prefix for URLs, usually /media/.
That should be all you need to do to automatically invoke Django’s JavaScript filter for any multiple select form fields that use the pop-up add hack. If you’re not using pop-up adds, you’ll need to override forms.SelectMultiple and create a template for the new Select, just as described in Replicating Django’s admin form pop-ups.
In response to Replicating Django’s admin form pop-ups: Django’s built-in admin form lets you “add another” item in a ForeignKey or ManyToManyField by clicking a plus button. This functionality is very easy to duplicate in your own forms.
- The Django admin site at Django
- “One of the most powerful parts of Django is the automatic admin interface. It reads metadata in your model to provide a powerful and production-ready interface that content producers can immediately use to start adding content to the site. In this document, we discuss how to activate, use and customize Django’s admin interface.”
- Replicating Django’s admin form pop-ups
- Django’s built-in admin form lets you “add another” item in a ForeignKey or ManyToManyField by clicking a plus button. This functionality is very easy to duplicate in your own forms.
More Django
- Django formsets and date/time fields
- Date/Time fields in Django formsets appear to have incompatible default values, resulting in forms using them always looking as though they’ve got a new entry when they don’t.
- Multiple Input Fields with multiple inheritance
- We needed to display one TextField as either a TextInput or a Textarea, depending on the value in the field. Multiple inheritance makes it easy, if a bit wonky.
- Django tutorial mostly ready
- My long-promised Django tutorial is pretty much ready. It’s still designed around an in-person tutorial, but you should be able to get started using it even if you’re on your own.
- Django: Beyond the SQL
- Django is a great application framework for Python and web applications. You can use it to greatly speed up your database and application development both on the web and on the command line. This tutorial is currently a very rough draft; it probably won’t be very useful without the assistance of someone who knows Django running the tutorial. If I ever run this tutorial a second time, I’ll probably update it with screenshots to make it more usable for individuals.
- Django actions as their own intermediate page
- The Django documentation recommends a complicated URL redirect to handle admin actions that need an intermediate page. But there’s no reason we can’t just piggy-back the intermediate page on the action itself.
- 22 more pages with the topic Django, and other related pages
