2009-11-04 10 views
8

Ich habe eine Stadt Fremdschlüssel in BusinessBranch-Modell. Das Modell "Mein Stadt" verfügt auch über Landes- und Landesschlüssel für Länder- und Bezirksmodelle. Es fällt mir schwer, in meiner BusinessBranchInline Status- und Länder-Dropdown-Menüs anzuzeigen. Was wäre der beste Weg, dies zu erreichen? Es wäre großartig, wenn die Dropdown-Elemente Elemente basierend auf dem Wert des übergeordneten Elements filtern.Land/Staat/Stadt Dropdown-Menüs innerhalb der Django Admin inline

django admin screenshot http://i33.tinypic.com/15n69mq.png

Antwort

23

Mit ein wenig hackery verwenden können, ist es durchaus machbar.

Im folgenden Beispiel County ist statt Staat und Gemeinde statt Stadt verwendet. So sind die Modelle wie folgt:

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")) 

Es gibt zwei Seiten des Problems: clientseitige JavaScript und Server-Seite Feld-Rendering.

Die Client-Seite JavaScript (mit JQuery, angenommen von /site_media/js/municipality.js bedient werden) ist wie folgt:

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 = '<option value="" selected="selected">---------</option>'; 
     for (var i in ret) { 
      options += '<option value="' + ret[i].id + '">' 
      + ret[i].name + '</option>'; 
     } 
     response_cache[county_id] = options; 
     $("#id_municipality").html(options); 
     }); 
    } 
} 

$(document).ready(function() { 
    $("#id_county").change(function() { fill_municipalities($(this).val()); }); 
}); 

Nun müssen Sie die Ajax Ansicht Gemeinden zu dienen, die gehören zu einem gegebenen Kreis (angenommen von /municipalities_for_county/ serviert werden):

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'}) 

Und schließlich der Server-seitigen Code in Admin. py zum Rendern des Feldes ist wie folgt. Zunächst werden die Importe:

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 

Dann wird das Widget:

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'<select%s>' % flatatt(final_attrs)] 
     options = self.render_options(choices, [value]) 
     if options: 
      output.append(options) 
     output.append('</select>') 
     return mark_safe(u'\n'.join(output)) 

Als nächstes wird die Form:

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 

Und schließlich die Admin-Klasse:

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) 

Lassen Sie mich wissen, wenn einige Ding bleibt unklar.

+5

+1 für so eine unglaublich gründliche Antwort.Ich werde etwas Ähnliches tun und obwohl ich den Admin nicht verwende, sollte das eine große Hilfe sein. Vielen Dank! –

+1

danke - ein Gentleman und Gelehrter. – snakesNbronies

+0

'mimitype' ist (django 1.6) veraltet. Ich denke, du musst es durch 'content_type' ersetzen. – suhailvs

0

Sie können in aussehen soll eine eigene „Adresse“ Widget zu erstellen, die die Kaskadierung mit drei Dropdown-Listen behandelt. Sie können sich den Quellcode für das Widget "DateTime" als Anleitung ansehen.

Werfen Sie auch einen Blick auf Tutorials zum Erstellen von benutzerdefinierten Widgets, z. B. this one.

0

Es wäre großartig, wenn die Dropdown-Elemente auf dem Wert des übergeordneten Elements filtern.

Sie Ajax Form Machine des dajaxproject für diesen Teil

Verwandte Themen