Django y selects encadenados

VM Vera Mazhuga Vera Mazhuga

Vera Mazhuga

Software Developer
1 min read.


Vamos a ver un ejemplo de cómo escribir formularios con selects encadenados en Django.


Hay casos cuando necesitamos escribir dos selects que dependen uno del otro. Por ejemplo, tenemos un modelo de países y otro de ciudades:

class Country(models.Model):
    name = models.CharField(
        max_length=100,
        verbose_name=u'nombre',
    )
class City(models.Model):
    name = models.CharField(
        max_length=100,
        verbose_name=u'nombre',
    )
    country = models.ForeignKey(
        'place.Country',
        verbose_name=u'país',
    )


y otro modelo que está relacionado con ciudades:

class Department(models.Model):
    name = models.CharField(
        max_length=100,
        verbose_name=u'nombre',
    )
    city = models.ForeignKey(
        'place.City',
        verbose_name=u'ciudad',
    )


Queremos crear un formulario para la creación de departamento en el que nuestro usuario pueda elegir primero un país y luego, de la lista de las ciudades filtrada por el país elegida, la ciudad.


En éste caso podemos usar un plugin para jQuery que nos permite encadenar dos selects: 
http://www.appelsiini.net/projects/chained.

$(function() {
    $("#id_city").chained("#id_country");
});


Para asociar los datos, el plugin Chained Selects necesita agregar un atributo 
class para el select de las ciudades en la que se indica el id del país correspondiente:


<pre data-gclp-id="0" data-initialized="true" lang="html"><br/><select id="id_country" name="country"><br/> <option value="" selected="selected">---------</option><br/> <option value="1">Colombia</option><br/> <option value="2">Rusia</option><br/></select><br/><select id="id_city" name="city"><br/> <option value="" selected="selected">---------</option><br/> <option value="1" class="1">Bogotá</option><br/> <option value="2" class="2">Moscú</option><br/> <option value="3" class="2">San Petersburgo</option><br/> <option value="4" class="1">Valledupar</option><br/></select><br/></pre>


Aquí se puede encontrar la demostración en CodePan: http://codepen.io/anon/pen/EapNPo.


Para hacer esto debemos redefinir el método 
render_option para el campo de la ciudad en forms.py:

class CityChainedSelectWidget(forms.Select) def render_option(self, selected_choices, option_value, option_label):<br/> option_value = force_text(option_value)<br/> if option_value in selected_choices:<br/> selected_html = mark_safe(u' selected="selected"')<br/> if not self.allow_multiple_selected:<br/> # Only allow for a single selection.<br/> selected_choices.remove(option_value)<br/> else:<br/> selected_html = u''<br/> customer_reference = u''<br/> if option_value:<br/> customer_id = City.objects.get(id=option_value).customer.id<br/> customer_reference = u' class={0}'.format(customer_id)<br/> return format_html(u'<option value="{0}"{1}{2}>{3}</option>',<br/> option_value,<br/> selected_html,<br/> customer_reference,<br/> force_text(option_label))<br/>class DepartmentForm(PersonForm):<br/> country = forms.ModelChoiceField(<br/> label=u'País',<br/> queryset=Country.objects.all(),<br/> )<br/> class Meta:<br/> model = Department<br/> fields = (<br/> 'name',<br/> 'city',<br/> )<br/> widgets = {<br/> 'city': CityChainedSelectWidget(),<br/> }<br/></pre>


Lo que hicimos fue reescribir el widget predeterminado de Django para los campos de select e indicar que al renderizar cada opción (elemento 
option) para cada ciudad, hay que agregar un nuevo atributo HTML class y asignar el id del país relacionado.


La imagen tomada de https://www.flickr.com/photos/mikeporesky/5432225640


Written by Vera Mazhuga

VM Vera Mazhuga Vera Mazhuga

Vera specializes in writing and maintaining code for various applications. Her focus on problem-solving and efficient programming ensures reliable and effective software solutions.

Newsletter

Subscribe to our newsletter:

Read more

Ascii Art

Algunas personas nos han preguntado acerca del batman que utilizamos en nuestro código fuente en todos los proyectos que real...

· 1 min read.

Build Once. Own Forever.