2009-11-04 15 views
8

Posiadam klucz obcy miejski wg modelu BusinessBranch. Model My City ma również zagraniczne klucze stanu i kraju dla modeli państwowych i powiatowych. Mam trudny czas wyświetlania menu rozwijanych stanu i kraju wewnątrz mojej BusinessBranchInline. Jaki byłby najlepszy sposób, aby to osiągnąć? Byłoby wspaniale, gdyby listy rozwijane filtrowały elementy w oparciu o wartość rodzica.Menu rozwijane Kraj/Stan/Miasto wewnątrz administratora Django w linii

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

Odpowiedz

23

Przy odrobinie hackery, to całkiem wykonalne.

W poniższym przykładzie County jest używany zamiast państwa i Gminy zamiast miasta. Tak więc modele są następujące:

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

Istnieją dwie strony problemu: renderowanie po stronie klienta po stronie serwera i po stronie serwera.

JavaScript po stronie klienta (z JQuery, zakłada się, że podawane /site_media/js/municipality.js) przedstawia się następująco:

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()); }); 
}); 

Teraz trzeba pogląd Ajax do obsługi gmin należących do danego okręgu (zakłada się, że podawane /municipalities_for_county/):

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

I wreszcie serwer kod boczny w administratora. py dla renderowania pola jest następująca. pierwszy import:

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 

Następnie widżet:

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

Następnie forma:

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 

I wreszcie, klasa admin:

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) 

Daj mi znać, jeśli niektóre rzecz pozostaje niejasna.

+5

+1 za tak niesamowicie dokładną odpowiedź.Mam zamiar zrobić coś podobnego i chociaż nie używam administratora, powinno to być bardzo pomocne. Dzięki! –

+1

dziękuję - dżentelmen i uczony. – snakesNbronies

+0

'mimitype' jest (django 1.6) przestarzałe. Myślę, że musisz zastąpić go 'content_type'. – suhailvs

0

Możesz zajrzeć do tworzenia niestandardowego „adres” widget, który obsługuje kaskadowego z trzech rozwijanego menu. Możesz zajrzeć do kodu źródłowego widgetu DateTime, aby uzyskać wskazówki na ten temat.

Zobacz także samouczki dotyczące tworzenia niestandardowych widżetów, takich jak this one.

0

Byłoby świetnie, gdyby listy rozwijane filtrowały elementy w oparciu o wartość elementu nadrzędnego.

Można użyć Ajax Form Machine z dajaxproject dla tej części