entre Desarrolladores

Recibe ayuda de expertos

Registrate y pregunta

Es gratis y fácil

Recibe respuestas

Respuestas, votos y comentarios

Vota y selecciona respuestas

Recibe puntos, vota y da la solución

Pregunta

0voto

Select dependiente en Django

Cómo hago para colocar cuatro select a mi template que muestren estado, municipio, parroquia y ciudad? y por supuesto, si el usuario selecciona el estado Aragua, el otro select traiga solamente los municipios, parroquias y ciudades de Aragua. Cómo lo hago?

2 Respuestas

0voto

pity7736 Puntos860

Eso se hace utilizando ajax, para que haga la petición al servidor y devuelva los datos sin recargar la página.

0voto

ibrames comentado

Comprendo. Y cómo lo hago?

2votos

white Puntos75880

Tomando en cuenta tu pregunta anterior:

¿Crear cuadro de búsqueda con Ajax en Django?

Saltaré ciertas cosas,


Necesitas una app o una vista que controle la petición ajax.

views.py

from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotFound
from <TuApp>.models import Ciudades, Estados, Municipios, Parroquias
import json

def geo(request, type = None, parent_id = None):

    if not request.is_ajax():
        return HttpResponseBadRequest('<h1>%s</h1>' % 'bad request')

    # para testear el efecto loading
    #time.sleep(1);

    locations = {

    ###################################################################
    #    Localidad => [modelo, modelo padre, foreignkey, parent type] #
    ###################################################################
        'estados' : [Estados],
        'ciudades' : [Ciudades, Estados, 'estado_id', 'estado'],
        'municipios' : [Municipios, Estados, 'estado_id', 'estado'],
        'parroquias' : [Parroquias, Municipios, 'municipio_id', 'municipio']
    }

    location_exists = False

    if parent_id != None:
        location_exists = (locations[type][2].objects.filter(id = parent_id).count() > 0)
    else:
        location_exists = (locations[type][0].objects.count() > 0)

    if not location_exists:
        return HttpResponseBadRequest('Identificador inválido')

    data_fields = {
        'estados' : ('id', 'estado'),
        'ciudades' : ('id', 'ciudad'),
        'municipios' : ('id', 'municipio'),
        'parroquias' : ('id', 'parroquia')
    }

    extra_where = None

    if parent_id != None:
        extra_where = ['%s = %d' % (locations[type][2], int(parent_id))]

    location_result = locations[type][0].objects.extra(where = extra_where).values(*data_fields[type]).order_by('id')

    if not location_result.count() > 0:
        return HttpResponseNotFound(json.dumps({'error' : 'empty'}))

    data = {
        'parent' : None,
        'data' : list(location_result)
    }

    if parent_id != None:
        data['parent'] = {
            'id' : parent_id,
            'type' : locations[type][3]
        }

    output = json.dumps(data)

    return HttpResponse(output, content_type = 'application/json')

reemplaza <TuApp> de: from <TuApp>.models import Ciudades, Estados, Municipios, Parroquias por el nombre de tu aplicación.

En el url dispatcher necesitas 2 reglas:

urls.py

url(r'^geo/(?P<type>estados)/$', home.views.geo, name='geo'),
url(r'^geo/(?P<type>ciudades|municipios|parroquias)/(?P<parent_id>[0-9]+)$', home.views.geo, name='geo'),

la expresion regular ^geo/(?P<type>ciudades|municipios|parroquias)/(?P<parent_id>[0-9]+)$ toma el tipo de localidad y el identificador de la localidad padre

ejemplo: /geo/ciudades/1, donde "1" es el identificador de un Estado.


Para los modelos hay 4 clases:

class Estados(models.Model):
class Ciudades(models.Model):
class Municipios(models.Model):
class Parroquias(models.Model):

models.py

class Estados(models.Model):
    id = models.AutoField(primary_key = True)
    estado = models.TextField(max_length = 255)
    iso_3166_2 = models.TextField(max_length = 4)

    def __str__(self):
        return '{0}'.format(self.estado)

class Ciudades(models.Model):
    id = models.AutoField(primary_key = True)
    estado = models.ForeignKey(Estados)
    ciudad = models.TextField(max_length = 255)
    capital = models.SmallIntegerField()

    def __str__(self):
        return '{0}'.format(self.ciudad)

class Municipios(models.Model):
    id = models.AutoField(primary_key = True)
    estado = models.ForeignKey(Estados)
    municipio = models.TextField(max_length = 100)

    def __str__(self):
        return '{0}'.format(self.municipio)

class Parroquias(models.Model):
    id = models.AutoField(primary_key = True)
    municipio = models.ForeignKey(Municipios)
    parroquia = models.TextField(max_length = 255)

    def __str__(self):
        return '{0}'.format(self.parroquia)

El codigo javascript controla los dropdown y la peticion ajax:

script.js

function set_multiselect()
{
    var multiselect = $('.multiselect');

    $(multiselect).find('select').each(function(){
        $(this).append(
            $('<option>')
            .val('0')
            .text($(this).attr('title'))
            .addClass('label')
        )
        .addClass('disabled')
        .prop('disabled', true)
    });

    get_geo(
        $(multiselect).find('select').first(),
        [$(multiselect).find('select').first().attr('data-query')]
    );

    $(multiselect).find('select').each(function(){

        $(this).on('change', function(){

            if(!$(this).val() || !$(this).next().is('select'))
                return false;

            var parent_val = false,
                query_val;

            if(typeof $(this).attr('data-parent') !== 'undefined')
                parent_val = $(multiselect).find('select[name=' + $(this).next().attr('data-parent') + ']').val();

            query_val  = parent_val || $(this).val();

            if(parseInt(query_val) === 0)
                return false;

            if($(this).children().length > 1)
            {
                var current = $(this);

                do
                {
                    current = $(current).next();
                    $(current).find('option:not(.label)').remove();
                }
                while(!$(current).is(':last-child'));
            }

            get_geo($(this).next(), [
                $(this).next().attr('data-query'),
                query_val
            ]);
        });
    });
}

var geo_cache = [];

function get_geo(dropdown, query)
{
    var fill_dropdown = function(data){

            for(var x in data)
            {
                $(dropdown).append(
                    $('<option>')
                    .val(data[x].id)
                    .text(data[x][$(dropdown).attr('name')])
                )
            }

            $(dropdown).prop('disabled', false).removeClass('disabled');
        };

    if(!geo_cache[query.join('.')])
        geo_cache[query.join('.')] = [];
    else
        return fill_dropdown(geo_cache[query.join('.')]);

    $(dropdown).addClass('loading');

    $.ajax({
        dataType : 'json',
        type : 'GET',
        url  : '/geo/' + query.join('/'),
        success : function(h) {

            fill_dropdown(h.data);
            geo_cache[query.join('.')] = h.data;

        },
        error : function(h) {

            if(h.error == 'empty')
            {
                $(dropdown).find('option:not(.label)').remove();
            }

        },
        complete : function()
        {
            $(dropdown).removeClass('loading');
        }
    });

}

$(document).ready(set_multiselect);

requisitos: Jquery

La estructura de los dropdown debe ser esta para funcionar:

<div class="multiselect">
    <select name="estado" data-query="estados" title="Estado"></select>
    <select name="ciudad" data-query="ciudades" data-parent="estado" title="Ciudad"></select>
    <select name="municipio" data-query="municipios" data-parent="estado" title="Municipio"></select>
    <select name="parroquia" data-query="parroquias" data-parent="municipio" title="Parroquia"></select>
</div>

data-query: tiene el primer parametro para la peticion ajax (ejemplo: /geo/ciudades/).
data-parent: tiene el nombre de la localidad padre.


Necesitarias hacer las consultas respectivas, con la base de datos de las localidades

Lista de ciudades de Venezuela para un select en Django

Te adjunto un dump de sqlite.

Demo online: https://warm-tor-8879.herokuapp.com/
Dump sqlite: https://db.tt/enBUJ70K

1voto

yoelduran comentado

Excelente tu codigo hermano, pero yo tengo un prequeño problemita implementando este codigo en mi app, resulta que lo estoy usando con la api forms de django 1.8, la cuestion esta cuando selecciono alguna opcion en el estado no me carga los municipios, ha solo le quite la parte de ciudades
quedando estado, municipio y parroquia

aqui te paso el codigo del forms

from django import forms
from smi.models import Estado,Municipio,Parroquia

class Perfil(forms.Form):

SEXO_OPTIONS = (
('H','Hombre'),
('M','Mujer'),)
estado = forms.ComboField(widget=forms.Select(attrs={'class':'form-control',
                                                    'data-query':'estados',
                                                    'title':'Estado'}))

municipio = forms.ComboField(widget=forms.Select(attrs={'class':'form-control',
                                                        'data-query':'municipios',
                                                        'data-parent':'estado',
                                                        'title':'Municipio'}))

parroquia = forms.ComboField(widget=forms.Select(attrs={'class':'form-control',
                                                        'data-query':'parroquias',
                                                        'data-parent':'municipio',
                                                        'title':'Parroquia'}))

cedula  = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
edad =forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
sexo =forms.ComboField(widget=forms.Select(attrs={'class':'form-control'},choices=SEXO_OPTIONS))
imagen = forms.ImageField(widget=forms.FileInput(attrs={'class':'form-control'}))

Por favor, accede o regístrate para responder a esta pregunta.

Otras Preguntas y Respuestas


...

Bienvenido a entre Desarrolladores, donde puedes realizar preguntas y recibir respuestas de otros miembros de la comunidad.

Conecta