Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>With a little hackery, it's quite doable.</p> <p>In the following example, <strong>County</strong> is used instead of <strong>State</strong> and <strong>Municipality</strong> instead of <strong>City</strong>. So, the models are as follows:</p> <pre><code>class County(models.Model): name = models.CharField(_('Name'), max_length=100, unique=True) class Municipality(models.Model): county = models.ForeignKey(County, verbose_name=_('County')) name = models.CharField(_('Name'), max_length=100) class Location(models.Model): name = models.CharField(max_length=100) county = models.ForeignKey(County, verbose_name=_('County')) municipality = models.ForeignKey(Municipality, verbose_name=_("Municipality")) </code></pre> <p>There are two sides of the problem: client-side JavaScript and server side field rendering.</p> <p>The client side JavaScript (with JQuery, assumed to be served from <strong>/site_media/js/municipality.js</strong>) is as follows:</p> <pre><code>var response_cache = {}; function fill_municipalities(county_id) { if (response_cache[county_id]) { $("#id_municipality").html(response_cache[county_id]); } else { $.getJSON("/municipalities_for_county/", {county_id: county_id}, function(ret, textStatus) { var options = '&lt;option value="" selected="selected"&gt;---------&lt;/option&gt;'; for (var i in ret) { options += '&lt;option value="' + ret[i].id + '"&gt;' + ret[i].name + '&lt;/option&gt;'; } response_cache[county_id] = options; $("#id_municipality").html(options); }); } } $(document).ready(function() { $("#id_county").change(function() { fill_municipalities($(this).val()); }); }); </code></pre> <p>Now you need the Ajax view for serving municipalities that belong to a given county (assumed to be served from <strong>/municipalities_for_county/</strong>):</p> <pre><code>from django.http import JSONResponse from django.utils.encoding import smart_unicode from django.utils import simplejson from myproject.places.models import Municipality def municipalities_for_county(request): if request.is_ajax() and request.GET and 'county_id' in request.GET: objs = Municipality.objects.filter(county=request.GET['county_id']) return JSONResponse([{'id': o.id, 'name': smart_unicode(o)} for o in objs]) else: return JSONResponse({'error': 'Not Ajax or no GET'}) </code></pre> <p>And finally the server side code in <em>admin.py</em> for rendering the field is as follows. First, the imports:</p> <pre><code>from django import forms from django.forms import widgets from django.forms.util import flatatt from django.utils.encoding import smart_unicode from django.utils.safestring import mark_safe from django.contrib import admin from django.utils.translation import ugettext_lazy from myproject.places.models import Municipality, Location </code></pre> <p>Then, the widget:</p> <pre><code>class MunicipalityChoiceWidget(widgets.Select): def render(self, name, value, attrs=None, choices=()): self.choices = [(u"", u"---------")] if value is None: # if no municipality has been previously selected, # render either an empty list or, if a county has # been selected, render its municipalities value = '' model_obj = self.form_instance.instance if model_obj and model_obj.county: for m in model_obj.county.municipality_set.all(): self.choices.append((m.id, smart_unicode(m))) else: # if a municipality X has been selected, # render only these municipalities, that belong # to X's county obj = Municipality.objects.get(id=value) for m in Municipality.objects.filter(county=obj.county): self.choices.append((m.id, smart_unicode(m))) # copy-paste from widgets.Select.render final_attrs = self.build_attrs(attrs, name=name) output = [u'&lt;select%s&gt;' % flatatt(final_attrs)] options = self.render_options(choices, [value]) if options: output.append(options) output.append('&lt;/select&gt;') return mark_safe(u'\n'.join(output)) </code></pre> <p>Next, the form:</p> <pre><code>class LocationForm(forms.ModelForm): municipality = forms.ModelChoiceField(Municipality.objects, widget=MunicipalityChoiceWidget(), label=ugettext_lazy("Municipality"), required=False) class Meta: model = Location def __init__(self, *args, **kwargs): """ We need access to the county field in the municipality widget, so we have to associate the form instance with the widget. """ super(LocationForm, self).__init__(*args, **kwargs) self.fields['municipality'].widget.form_instance = self </code></pre> <p>And finally, the admin class:</p> <pre><code>class LocationAdmin(admin.ModelAdmin): form = LocationForm class Media: js = ('http://ajax.googleapis.com/ajax/libs/jquery/1.4.0/jquery.min.js', '/site_media/js/municipality.js') admin.site.register(Location, LocationAdmin) </code></pre> <p>Let me know if something remains unclear.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload