jueves, 4 de abril de 2019

Ventanas Modales con Django usando Bootstrap y Vistas Basadas en Clases




Como podrás ver en las imágenes del inicio, este artículo trata de poder adaptar el proyecto desarrollado con Python usando Django, para utilizar ventanas modales.

Primero lo primero, debemos tener las librerías necesarias agregadas al proyecto.  Esto sería, jQuery y BootStrap.

<!-- Bootstrap Core CSS -->
<link rel="stylesheet" href="{% static 'base/css/bootstrap.min.css' %}">

<!-- jQuery -->
<script src="{% static 'base/js/jquery.min.js' %}"></script>

<!-- Bootstrap Core JavaScript -->
<script src="{% static 'base/js/bootstrap.min.js' %}"></script>


Luego, ya el proyecto en Django, trabajaremos con las SubCategoría, así que el proyecto contiene lo siguiente:

models.py
class SubCategoria(ClaseModelo):
categoria = models.ForeignKey(Categoria, on_delete=models.CASCADE)
descripcion = models.CharField(
max_length=100,
help_text='Descripción de la Sub Categoría'
)

def __str__(self):
return '{}:{}'.format(self.categoria.descripcion,self.descripcion)

def save(self):
self.descripcion = self.descripcion.upper()
super(SubCategoria, self).save()

class Meta:
verbose_name_plural = "Sub Categorías"
unique_together = ('categoria','descripcion')


views.py
class SubCategoriaView(LoginRequiredMixin, generic.ListView):
model = SubCategoria
template_name = "catalogos/subcategoria_list.html"
context_object_name = 'obj'
login_url = 'generales:login'


class SubCategoriaNew(SinPrivilegios, LoginRequiredMixin, generic.CreateView):
permission_required = "catalogos.add_subcategoria"
model = SubCategoria
template_name = "catalogos/subcategoria_form.html"
context_object_name = 'obj'
form_class = SubCategoriaForm
success_url = reverse_lazy("catalogos:subcategoria_list")


class SubCategoriaEdit(SinPrivilegios, generic.UpdateView):
permission_required = "catalogos.change_subcategoria"
model = SubCategoria
template_name = "catalogos/subcategoria_form.html"
context_object_name = 'obj'
form_class = SubCategoriaForm
success_url = reverse_lazy("catalogos:subcategoria_list")

urls.py
path('subcategorias', SubCategoriaView.as_view() , name='subcategoria_list'),
path('subcategorias/new', SubCategoriaNew.as_view() , name='subcategoria_new'),
path('subcategorias/edit/<int:pk>', SubCategoriaEdit.as_view(),
name='subcategoria_edit'),

Ahora debemos de hacer algunos cambios en el proyecto, principalmente en la plantilla base (puede ser en una plantilla específica, pero prefiero que ya esté habilitado para cualquier plantilla que herede de la base).  En dicha plantilla agregaremos dos cosas:
  • El div donde se "renderizará" la ventana modal
  • Funciones en JavaScript necesarias para abrir y cerrar la ventana modal
Todo lo anterior, lo puse al final del body de la plantilla base.



base.html
<div id="ventana_modal" class="modal fade" role="dialog"></div>

<script>
function abrir_modal(url)
{
$('#ventana_modal').load(url, function()
{
$(this).modal({
backdrop: 'static',
keyboard: false
})
$(this).modal('show');
});
return false;
}

function cerrar_modal()
{
$('#ventana_modal').modal('hide');
return false;
}
</script>

Notar que la función encargada de abrir el modal, tiene también la tarea de activar las opciones backdrop y keyboard, esto le indica a la ventana modal de bootstrap que no se cierre al hacer click fuera de ella.

El div con id ventana_modal, es donde se "renderizará" la plantilla y tiene el efecto fade, para cuando se cargue dicho modal.

Ahora, debemos corregir las plantillas, para que se haga el llamado a la función abrir_modal y ésta a su vez despliegue la ventana modal.

A como pudiste haber visto en el archivo de rutas, para mostrar todas las categorías se utiliza la ruta llamada subcategoria_list, quien a su vez utiliza la vista SubCategoriaView, donde está declarada la plantilla catalogos/subcategoria_list.html, la cual es la encargada de mostrar todas las subcategorías y a su vez posee los botones para crear o editar y a la vez hereda de la plantilla base.

subcategoria_list.html
{% extends 'base/base.html' %} {% block page_header %} Listado de Sub Categorías {% endblock page_header %}
{% block contenido %}
<div class="panel panel-default">
<div class="panel-heading">
<a class="btn btn-warning" onclick="return abrir_modal('{% url 'catalogos:subcategoria_new' %}')" href="#"><span class="fa fa-plus-circle"></span> Nueva SubCategoría en PopPup</a>
</div>
<!-- /.panel-heading -->
<div class="panel-body">
<table width="100%" class="table table-striped table-bordered table-hover" id="dataTables-example">
<thead>
<tr>
<th>Id</th>
<th>Categoría</th>
<th>Descripción</th>
<th>Estado</th>
<th>Creado</th>
<th>Modificado</th>
<th class="all">Acciones</th>
</tr>
</thead>
<tbody>
{% for item in obj %}
<tr>
<td>{{item.id}}</td>
<td>{{item.categoria}}</td>
<td>{{item.descripcion}}</td>
<td>{{item.activo|yesno:"Activo,Inactivo"}}</td>
<td>{{item.creado|date:'d/m/Y H:i'}}</td>
<td>{{item.modificado|date:'d/m/Y H:i'}}</td>
<td>
<a onclick="return abrir_modal ('{% url 'catalogos:subcategoria_edit' item.id %}')" class="btn btn-primary btn-circle" href="#">
<span class="fa fa-edit"></span>
</a>
<a href="{% url 'catalogos:categoria_del' item.id %}" class="btn btn-danger btn-circle">
<span class="fa fa-trash"></span>
</a>
</td>
</tr>
{% endfor%}
</tbody>
</table>
</div>
</div>
{% endblock contenido%}

De esta plantilla debes notar los botones para nueva y editar subcategoría.

<a class="btn btn-warning"  
onclick="return abrir_modal('{% url 'catalogos:subcategoria_new' %}')" 
href="#"><span class="fa fa-plus-circle">
</span> Nueva SubCategoría en PopPup 
</a>


<a  
onclick="return abrir_modal ('{% url 'catalogos:subcategoria_edit' item.id %}')" 
class="btn btn-primary btn-circle" href="#">
<span class="fa fa-edit"></span>
</a>


En cada uno de los botones se hace el llamado a la función abrir_modal, a quien se la pasa la ruta a mostrar.

Aunque ambas rutas apunten a dos vistas de clases diferentes, porque una hará el INSERT y la otra el UPATE. Lo que si tienen en común es que ambas utilizan la plantilla catalogos/subcategoria_form.html


Esta plantilla es igual a las que hayas podido trabajar en tus proyectos y es igual a la que trabajamos en el curso de Desarrollo Web con Python usando Django, con la diferencia que en el form se debe indicar como acción la ruta de la url para cada acción, por tanto se hace un pequeño truco, si la variable de contexto obj existe, quiere decir que el formulario hará una actualización, porque trae registros, si por lo contrario no existe, quiere decir que obj no existe o está vacío y se tratará de una inserción de registros.

{% if obj %}
<form role="form" action="{% url 'catalogos:subcategoria_edit' obj.pk %}" method="post" class="form-inline">
{% else %}
<form role="form" action="{% url 'catalogos:subcategoria_new' %}" method="post" class="form-inline">
{% endif %}

Este punto es muy importante, sino no podrás guardar o editar, el contenido completo del archivo es:

subcategoria_form.html
<div class="modal-dialog modal-lg">
<div class="modal-content">
{% if obj %}
<form role="form" action="{% url 'catalogos:subcategoria_edit' obj.pk %}" method="post" class="form-inline">
{% else %}
<form role="form" action="{% url 'catalogos:subcategoria_new' %}" method="post" class="form-inline">
{% endif %}
<div class="panel panel-default">
<div class="panel-heading">
Editar/Crear Categoría
</div>
<!-- /.panel-heading -->
<div class="panel-body">
{% csrf_token %} {{form.as_p}}
<button type="submit" class="btn btn-danger"><span class="fa fa-save"></span> Guardar</button>
<a href="{% url 'catalogos:subcategoria_list' %}" class="btn btn-success"><span class="fa fa-undo"></span> Cancelar</a>
</div>
</div>
</form>
</div>
</div>


Noten que esta plantilla NO HEREDA de base, ya que si lo hiciera, se mostrará la plantilla completa y no se verá el "efecto" modal, es por esa razón que no hereda.

Otro detalle son los dos primeros div de la plantilla, completan la estructura de una ventana modal de bootstrap (con los cierres correspondientes).

<div class="modal-dialog modal-lg">
<div class="modal-content">



Ya con eso podrás implementar ventanas modales de bootstrap utilizando Vistas Basadas en Clases.

El código fuente completo forma parte del curso Desarrollo Web con Python usando Django, curso en constante actualización y si te registras desde este sitio, obtendrás un 95% de descuento del precio total.  De USD 199.99 a sólo USD 9.99 o menos según el país donde te encuentres.

Te animo a inscribirte, ya que es un curso en constante actualización.
http://bit.ly/dj-3-2019-95p


También puedes ver otros dos cursos sobre Django, en los cuales se amplían el conocimiento y se explota más este magífico FrameWork para Web de Python.





Elabora RestFul API con Python usando Django Rest FrameWork



http://bit.ly/ORM4-2019





Domina el ORM de Django, para que sepas manejar mejor la forma en que Django interactúa con tu Motor de Base de Datos.
http://bit.ly/RestFulDjango022019

0 comentarios:

Publicar un comentario

¿Tienes algún comentario? ¿Qué te ha parecido este artículo? Cuéntalo.

 
>