Des de revisar el ejemplo oficial de Django, he notado que sigue una arquitectura MVC. Para quienes no estén familiarizados con este patrón, les recomiendo investigarlo antes de continuar.
La documentación oficial explica cómo crear un proyecto, incluyendo migraciones de base de datos. Utilicé Django 2.1 con Python 3.6 para implementar operaciones CRUD básicas.
Instalación de Django
Primero, instalamos Django:
pip install django
Creación del proyecto
Para crear un nuevo proyecto:
django-admin startproject mi_proyecto
La estructura de archivos generada se detalla en la documentación oficial.
Problemas encontrados
Configuración de la base de datos
Dado que mi servicio MySQL está en una máquina virtual local, la configuración en settings.py quedó así:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'basededatos_django',
'HOST': '192.168.175.130',
'USER': 'root',
'PASSWORD': '123456',
'CHARSET': 'latin1',
}
}
Definición del modelo
A continuación, definí mi modelo:
class Recursos(models.Model):
id_recurso = models.IntegerField()
enlace = models.CharField(max_length=255)
id_categoria = models.SmallIntegerField()
titulo = models.CharField(max_length=255)
descripcion = models.TextField()
def __str__(self):
return self.titulo
Configuración de la aplicación
En apps.py:
class AdministracionConfig(AppConfig):
name = 'administracion'
Y en settings.py, importé la aplicación:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog.apps.BlogConfig',
'administracion.apps.AdministracionConfig',
]
Migraciones de base de datos
Ejecuté:
manage.py makemigrations administracion
Este comando genera el SQL para crear tablas basado en el modelo.
Luego ejecuté:
manage.py migrate
Problema con MySQLdb
Al ejecutar la migración, encontré que faltaba la librería MySQLdb. Intenté instalarla con:
pip install python-mysql
Obtuve varios errores. Después de revisar la documentación, descubrí que para Python 3.6 ya no existe este módulo. En su lugar, necesitamos instalar PyMySQL:
pip install pymysql
Luego, añadí esta línea a settings.py:
import os, pymysql
pymysql.install_as_MySQLdb()
Con esto, todo funcionó correctamente.
Vistas y plantillas
Plantilla index.html
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gestión de Recursos</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
<style>
.navegador-superior {
background-color: aqua;
}
.navegador-izquierdo {
background-color: #fff;
}
.contenido-principal {
background-color: #f8f9fa;
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-12 navegador-superior">
<p>Encabezado</p>
</div>
<div class="col-md-2 navegador-izquierdo">
<p>Menú lateral</p>
</div>
<div class="col-md-10 contenido-principal">
<div class="row">
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#modalAgregar">
AGREGAR
</button>
</div>
<div class="modal fade" id="modalAgregar" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
<h4 class="modal-title">Agregar Nuevo Recurso</h4>
</div>
<div class="modal-body">
<form id="formulario-recurso" class="form-horizontal" action="{% url 'administracion:agregarRecurso' %}" method="post" enctype="multipart/form-data">
<input type="hidden" name="id_recurso" value="1">
<input type="hidden" name="id_categoria" value="1">
{% csrf_token %}
<div class="form-group">
<label class="col-sm-2 control-label">Título</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="titulo">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Descripción</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="descripcion">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Archivo</label>
<div class="col-sm-10">
<input type="file" class="form-control" name="archivo">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancelar</button>
<button type="button" class="btn btn-primary" id="enviar-formulario">Guardar</button>
</div>
</div>
</div>
</div>
<div class="row">
<table class="table table-hover">
<tr>
<th>ID Recurso</th>
<th>Título</th>
<th>ID Categoría</th>
<th>Descripción</th>
<th>Imagen</th>
<th>Acciones</th>
</tr>
{% for recurso in recursos %}
<tr>
<th>{{ recurso.id_recurso }}</th>
<th>{{ recurso.titulo }}</th>
<th>{{ recurso.id_categoria }}</th>
<th>{{ recurso.descripcion }}</th>
<th><img src="{{ recurso.enlace }}" style="width: 50px;" alt=""></th>
<th>
<button class="btn btn-success editar" data-id="{{ recurso.id }}" data-titulo="{{ recurso.titulo }}" data-descripcion="{{ recurso.descripcion }}">Editar</button>
<button class="btn btn-danger eliminar" data-id="{{ recurso.id }}">Eliminar</button>
</th>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
</div>
</body>
<script>
$('#enviar-formulario').click(function () {
$('#formulario-recurso').submit();
})
$('.eliminar').click(function () {
var boton = $(this)
var id_recurso = boton.attr('data-id')
var token_csrf = jQuery("[name=csrfmiddlewaretoken]").val()
$.ajax({
beforeSend: function (xhr, settings) {
xhr.setRequestHeader("X-CSRFToken", token_csrf);
},
url: "{% url 'administracion:eliminarRecurso' %}",
data: {id: id_recurso},
type: 'post',
dataType: 'json',
success: function (respuesta) {
if (respuesta.codigo === 1){
alert('Eliminación exitosa')
}else{
alert('Error al eliminar')
}
location.reload()
}
})
})
</script>
</html>
Configuración de URLs
from django.urls import path
from . import vistas
app_name = 'administracion'
urlpatterns = [
path('', vistas.inicio, name='inicio'),
path('recursos/listar', vistas.listarRecursos, name='listarRecursos'),
path('recursos/agregar', vistas.agregarRecurso, name='agregarRecurso'),
path('recursos/eliminar', vistas.eliminarRecurso, name='eliminarRecurso')
]
Vistas
from django.shortcuts import render
from django.http import HttpResponse, Http404, HttpResponseRedirect, JsonResponse
from django.urls import reverse
from .models import Recursos
import os, uuid
def inicio(request):
return HttpResponse('Hola mundo con Django')
def listarRecursos(request):
recursos = Recursos.objects.all()
contexto = {"recursos": recursos}
return render(request, 'recursos/index.html', contexto)
def agregarRecurso(request):
id_recurso = request.POST.get('id_recurso', None)
id_categoria = request.POST.get('id_categoria', None)
titulo = request.POST.get('titulo', None)
descripcion = request.POST.get('descripcion', None)
enlace = subirArchivo(request.FILES['archivo'])
nuevo_recurso = Recursos(
id_recurso=id_recurso,
id_categoria=id_categoria,
titulo=titulo,
descripcion=descripcion,
enlace=enlace
)
nuevo_recurso.save()
if nuevo_recurso.id is None:
raise Http404
return HttpResponseRedirect(reverse('administracion:listarRecursos'))
def eliminarRecurso(request):
id_recurso = request.POST.get('id', None)
if id_recurso is None:
return JsonResponse({'codigo': 0})
try:
recurso = Recursos.objects.get(pk=id_recurso)
recurso.delete()
except Exception as e:
return JsonResponse({'codigo': 0})
else:
return JsonResponse({'codigo': 1})
def subirArchivo(archivo):
ruta = '/recursos/'
enlace_base = 'http://127.0.0.1:81/'
if not os.path.exists(ruta):
os.mkdir(ruta)
nombre_archivo = str(uuid.uuid1()) + archivo.name
ruta_guardado = ruta + nombre_archivo
with open(ruta_guardado, 'wb+') as archivo_guardado:
for fragmento in archivo.chunks():
archivo_guardado.write(fragmento)
return enlace_base + nombre_archivo
Problemas con codificación de caracteres
Al no especificar el conjunto de caracteres de la base de datos, encontré el error:
1366, "Incorrect string value: '\\xE6\\xB5\\x8B\\xE8\\xAF\\x95' for column 'titulo' at row 1"
Al revisar la configuración de MySQL:
mysql> show variables like '%char%';
Descubrí que muchos valores usaban latin1 por defecto. Mi tabla también estaba en Latin1. Busqué en la documentación de Django cómo configurar el charset a nivel de modelo, pero no encontré una solución directa.
Finalmente, opté por modificar la configuración de la base de datos. Para quienes enfrenten el mismo problema, pueden encontrar una guía aquí: https://www.cnblogs.com/HondaHsu/p/3640180.html
Después de estos cambios, la inserción de caracteres especiales funcionó correctamente.
Impresiones finales
Hasta este punto, siento que Django es un framwork completo pero algo pesado. Su curva de aprendizaje inicial parece considerable. Por esta razón, mi próximo proyecto será una exploración de Flask, un framework más ligero.