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
- Converting an existing Django model to Django-MPTT
- Using a SQL database to mimic a filesystem will, eventually, create bottlenecks when it comes to traversing the filesystem. One solution is modified preordered tree traversal, which saves the tree structure in an easily-used manner inside the model.
- Two search bookmarklets for Django
- Bookmarklets—JavaScript code in a bookmark—can make working with big Django databases much easier.
- Fixing Django’s feed generator without hacking Django
- It looks like it’s going to be a while before the RSS feed generator in Django is going to get fixed, so I looked into subclassing as a way of getting a working guid in my Django RSS feeds.
- ModelForms and FormViews
- This is just a notice because when I did a search, nothing came up. Don’t use ModelForm with FormView, use UpdateView instead.
- Django: fix_ampersands and abbreviations
- The fix_ampersands filter will miss some cases where ampersands need to be replaced.
- 29 more pages with the topic Django, and other related pages