Code


Version 8 (modified by mattimustang@…, 8 years ago) (diff)

linked to new attachment

An AJAX Select Widget for Django

This widget was written to replace the standard <select> form element Django uses for releated objects in forms. The widget is an extended version of the Dojo Select widget. I use this mostly in places where the number of objects in the related objects table exceeds 20 or more. The widget is extracted from a project where some tables have more than 30,000 objects and using a <select> dropdown simply did not scale. This widget supports live lookup of related fields based on a single field in that model and autocompletion.

Credit goes to Eric Moritz's original implementation for inspiring this one.

Screenshots

The selection available after clicking the down arrow:

screenshot of widget in use with no text input

Selections filtered after typing the letter b:

screenshot of widget in use after typing 'b'.

Requirements

Installation

  • Install Dojo in media/js.
  • Untar nongselect-1.0.tar.gz and copy the nong directory into the same parent directory as dojo and copy the contents of views.py into one of your own views.py files.

My project layout is as follows:

myproject/
    myapp/
        models.py
        views.py
        urls.py
        templatetags/
            formtags.py
        templates/
            myapp/
                mymodel_form.html
            widget/
                fieldrow.html
                selectrow.html
    media/
        js/
            dojo/
            nong/

Example Use

In this example we will create a form that uses the Select widget to for the Article's reporter field in below.

Model

Using the example model from Model Examples:

from django.db import models

class Reporter(models.Model):
    first_name = models.CharField(maxlength=30)
    last_name = models.CharField(maxlength=30)
    email = models.EmailField()

    def __str__(self):
        return "%s %s" % (self.first_name, self.last_name)

class Article(models.Model):
    headline = models.CharField(maxlength=100)
    pub_date = models.DateField()
    reporter = models.ForeignKey(Reporter)

    def __str__(self):
        return self.headline

URLconf

Add a url to the applications urls.py for the reporter_lookup view like so:

from myproject.myapp.models import Reporter

reporter_lookup = {
        'queryset': Reporter.objects.all(),
        'field': 'first_name', # this is the field which is searched
        #'limit': 10, # default is to limit query to 10 results. Increase this if you like.
        #'login_required': False, # default is to allow anonymous queries. Set to True if you want authenticated access.
}

urlpatterns = patterns('',
        (r'^reporter_lookup/$', 'myproject.myapp.views.json_lookup', reporter_lookup),
)

Now test your new reporter_lookup view my going to http://localhost:8000/myapp/reporter_lookup/?q=, which should return something like:

[["Reporter 1", 1], ["Reporter 2", 2], ...]

Template

Normally you would have just put {{ form.reporter }} somewhere in your article_form.html template to get a drop down menu with a list of the Reporters. To use the new widget in your form you'll need to add the following javascript and templatetags to your template:

...
{% load formtags %}
{% block extrahead %}
{% comment %}
load dojo and the select widget
{% endcomment %}
<script type="text/javascript" src="/media/js/dojo/dojo.js"></script>
<script type="text/javascript">
    dojo.require("dojo.widget.*");
    dojo.setModulePrefix("nong.widget","../nong/widget");
    dojo.widget.manager.registerWidgetPackage("nong.widget");
    dojo.require("nong.widget.NongSelect");
</script>
{% endblock %}
...
{% comment %}
Two templatetags are included for your convenience. The first, {% fieldrow ... %} splits out a form element similar in style to the
Django Admin with the field's help_text displayed.
The second, {% selectrow ... %}, splits out all the markup and javascript necessary for the AJAX select widget.

Replace the usual {{ form.reporter }} element in the template with this:
{% endcomment %}
{% fieldrow form.headline %}
{% fieldrow form.pub_date %}
{% selectrow form.reporter "/myapp/reporter_lookup/?q=%{searchString}" "first_name" %}
...

That's it! You should be able to submit this form just like you normally would.

Matthew Flanagan (mattimustang A T gmail.com)

Attachments (6)

Download all attachments as: .zip