Un sistema completo de gestión de productos desarrollado con Django y MySQL, que incluye funcionalidades CRUD, búsqueda avanzada, relaciones entre modelos y una interfaz de usuario moderna y responsive.
- Características
- Tecnologías Utilizadas
- Estructura del Proyecto
- Instalación y Configuración
- Uso del Sistema
- Modelos de Datos
- API y Vistas
- Funcionalidades Destacadas
- Capturas de Pantalla
- Contribución
- Licencia
- CRUD Completo: Crear, leer, actualizar y eliminar productos, categorías y etiquetas
- Búsqueda Avanzada: Búsqueda inteligente en múltiples campos (nombre, descripción, categoría, etiquetas)
- Relaciones de Datos: Implementación de relaciones OneToOne, ForeignKey y ManyToMany
- Interfaz Responsive: Diseño adaptativo para dispositivos móviles y escritorio
- Administración Django: Panel de administración personalizado con inlines
- Validaciones: Validación tanto del lado cliente como del servidor
- Mensajes Flash: Notificaciones user-friendly con SweetAlert2
- Diseño Moderno: Interfaz limpia con Bootstrap 5.3.3
- Iconografía: Bootstrap Icons para mejor experiencia visual
- Confirmaciones: Diálogos de confirmación para acciones destructivas
- Feedback Visual: Badges, alertas y mensajes informativos
- Navegación Intuitiva: Estructura de navegación clara y consistente
- Django 5.2.7: Framework web de Python
- MySQL: Base de datos relacional
- Python 3.13: Lenguaje de programación
- Bootstrap 5.3.3: Framework CSS para diseño responsive
- Bootstrap Icons 1.11.3: Iconografía
- jQuery 3.7.1: Biblioteca JavaScript
- SweetAlert2: Diálogos y alertas modernas
- Django ORM: Mapeo objeto-relacional
- Django Admin: Panel de administración
- Django Forms: Validación y renderizado de formularios
- Django Messages: Sistema de mensajes flash
eval_mod_m7/
├── 📄 manage.py # Script de gestión de Django
├── 📄 README.md # Documentación del proyecto
├── 📁 ProyGestorProductos/ # Configuración del proyecto
│ ├── 📄 __init__.py
│ ├── 📄 settings.py # Configuraciones de Django
│ ├── 📄 urls.py # URLs principales
│ ├── 📄 wsgi.py # Configuración WSGI
│ └── 📄 asgi.py # Configuración ASGI
├── 📁 app_productos/ # Aplicación principal
│ ├── 📄 models.py # Modelos de datos
│ ├── 📄 views.py # Vistas de la aplicación
│ ├── 📄 urls.py # URLs de la aplicación
│ ├── 📄 forms.py # Formularios Django
│ ├── 📄 admin.py # Configuración del admin
│ ├── 📄 apps.py # Configuración de la app
│ ├── 📁 migrations/ # Migraciones de base de datos
│ └── 📁 templates/ # Templates HTML
│ ├── 📄 list.html # Lista de elementos
│ ├── 📄 form.html # Formularios
│ ├── 📄 detail.html # Detalle de elementos
│ └── 📄 confirmar_eliminacion.html
├── 📁 templates/ # Templates globales
│ ├── 📄 base.html # Template base
│ └── 📄 index.html # Página de inicio
└── 📁 static/ # Archivos estáticos
└── 📁 css/
└── 📄 styles.css # Estilos personalizados
- Python 3.13+
- MySQL 8.0+
- pip (gestor de paquetes de Python)
git clone https://github.com/darksea48/M7_AE6_AEPRO_ONG.git
cd eval_mod_m7# Windows
python -m venv venv
venv\Scripts\activate
# Linux/Mac
python3 -m venv venv
source venv/bin/activatepip install django==5.2.7
pip install mysqlclientCREATE DATABASE gestorproductos CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'gestorproductos',
'USER': 'tu_usuario',
'PASSWORD': 'tu_contraseña',
'HOST': 'localhost',
'PORT': '3306',
}
}python manage.py makemigrations
python manage.py migratepython manage.py createsuperuserpython manage.py runserverLa aplicación estará disponible en: http://127.0.0.1:8000/
- Página Principal:
http://127.0.0.1:8000/ - Lista de Productos:
http://127.0.0.1:8000/productos/ - Panel de Admin:
http://127.0.0.1:8000/admin/
- Productos: Gestionar inventario de productos
- Categorías: Organizar productos por categorías
- Etiquetas: Sistema de etiquetado flexible
- Búsqueda: Búsqueda avanzada en múltiples campos (nombre, descripción, categoría, etiquetas)
- ✅ Crear nuevo producto con detalles físicos
- ✅ Listar productos con información resumida
- ✅ Ver detalles completos de un producto
- ✅ Editar información de productos existentes
- ✅ Eliminar productos con confirmación
- ✅ Buscar productos por múltiples criterios
- ✅ Crear y organizar categorías
- ✅ Ver conteo de productos por categoría
- ✅ Editar información de categorías
- ✅ Eliminar categorías (con validación de dependencias)
- ✅ Sistema flexible de etiquetado
- ✅ Relación muchos-a-muchos con productos
- ✅ Conteo de productos por etiqueta
- ✅ Gestión completa CRUD
class Producto(models.Model):
nombre = models.CharField(max_length=200)
descripcion = models.TextField()
precio = models.DecimalField(max_digits=10, decimal_places=2)
categoria = models.ForeignKey(Categoria, on_delete=models.SET_NULL, null=True)
etiquetas = models.ManyToManyField(Etiqueta, blank=True)
fecha_creacion = models.DateTimeField(auto_now_add=True)
fecha_actualizacion = models.DateTimeField(auto_now=True)class Categoria(models.Model):
nombre = models.CharField(max_length=100)
descripcion = models.TextField(blank=True, null=True)class Etiqueta(models.Model):
nombre = models.CharField(max_length=100, unique=True)class DetalleProducto(models.Model):
producto = models.OneToOneField(Producto, on_delete=models.CASCADE, primary_key=True)
peso = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True)
ancho = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True)
alto = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True)
largo = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True)- Uno a Uno (1:1) con el campo OneToOneField: Producto ↔ DetalleProducto
- Uno a Muchos (1:N) con el campo ForeignKey: Producto → Categoría (Many-to-One)
- Muchos a Muchos (N:M) con el campo ManyToManyField: Producto ↔ Etiqueta
ProductoListView: Lista de productos con paginaciónProductoCreateView: Creación de productosProductoUpdateView: Edición de productosProductoDetailView: Detalle de productoCategoriaListView: Lista de categorías con conteoEtiquetaListView: Lista de etiquetas con conteoCategoriaProductosListView: Lista de productos por categoríaEtiquetaProductosListView: Lista de productos por etiqueta
buscar_productos: Búsqueda avanzada con Q objectseliminar_producto: Eliminación con confirmacióneliminar_categoria: Eliminación de categoríaseliminar_etiqueta: Eliminación de etiquetas
urlpatterns = [
# Productos
path('productos/', views.ProductoListView.as_view(), name='lista_productos'),
path('productos/crear/', views.ProductoCreateView.as_view(), name='crear_producto'),
path('productos/<int:pk>/', views.ProductoDetailView.as_view(), name='detalle_producto'),
path('productos/<int:pk>/editar/', views.ProductoUpdateView.as_view(), name='editar_producto'),
path('productos/<int:id>/eliminar/', views.eliminar_producto, name='eliminar_producto'),
path('productos/buscar/', views.buscar_productos, name='buscar_productos'),
# Categorías
path('categorias/', views.CategoriaListView.as_view(), name='lista_categorias'),
path('categorias/crear/', views.CategoriaCreateView.as_view(), name='crear_categoria'),
path('categorias/<int:pk>/editar/', views.CategoriaUpdateView.as_view(), name='editar_categoria'),
path('categorias/<int:id>/eliminar/', views.eliminar_categoria, name='eliminar_categoria'),
path('categorias/<int:pk>/productos/', views.CategoriaProductosListView.as_view(), name='categoria_productos'),
# Etiquetas
path('etiquetas/', views.EtiquetaListView.as_view(), name='lista_etiquetas'),
path('etiquetas/crear/', views.EtiquetaCreateView.as_view(), name='crear_etiqueta'),
path('etiquetas/<int:pk>/editar/', views.EtiquetaUpdateView.as_view(), name='editar_etiqueta'),
path('etiquetas/<int:id>/eliminar/', views.eliminar_etiqueta, name='eliminar_etiqueta'),
path('etiquetas/<int:pk>/productos/', views.EtiquetaProductosListView.as_view(), name='etiqueta_productos'),
]- Múltiples campos: Busca en nombre, descripción, categoría y etiquetas
- Búsqueda insensible: No distingue mayúsculas/minúsculas
- Coincidencias parciales: Encuentra términos dentro de palabras
- Sin duplicados: Usa
distinct()para evitar repeticiones - Feedback visual: Muestra cantidad de resultados encontrados
# Implementación de búsqueda
productos_encontrados = Producto.objects.filter(
Q(nombre__icontains=query) |
Q(descripcion__icontains=query) |
Q(categoria__nombre__icontains=query) |
Q(etiquetas__nombre__icontains=query)
).distinct()- Productos por categoría: Usando
annotate()yCount() - Productos por etiqueta: Optimización de consultas SQL
- Badges visuales: Indicadores coloridos según la cantidad
# Optimización con annotate
categorias_con_conteo = Categoria.objects.annotate(
total_productos=Count('productos')
).order_by('nombre')- Producto + DetalleProducto: Un solo formulario para ambos modelos
- Validación personalizada: Lógica de negocio en el formulario
- Manejo de relaciones: Gestión automática de OneToOne
- SweetAlert2: Confirmaciones elegantes para eliminaciones
- Bootstrap 5: Diseño moderno y responsive
- Mensajes Flash: Feedback inmediato de acciones
- Navegación intuitiva: Estructura clara y consistente
- Dashboard con acceso rápido a todas las funcionalidades
- Navegación clara y organizada
- Enlaces directos a crear elementos
- Vista de tarjetas responsive
- Información resumida de cada producto
- Botones de acción (Ver, Editar, Eliminar)
- Barra de búsqueda integrada
- Campos unificados para Producto y DetalleProducto
- Validación en tiempo real
- Selección de categoría y etiquetas
- Diseño responsive y accessible
- Admin de Django personalizado
- Inlines para DetalleProducto
- Filtros y búsquedas optimizadas
- Gestión completa de relaciones
- Fork el repositorio
- Crea una rama para tu feature (
git checkout -b feature/nueva-funcionalidad) - Commit tus cambios (
git commit -m 'Agrega nueva funcionalidad') - Push a la rama (
git push origin feature/nueva-funcionalidad) - Abre un Pull Request
- Seguir PEP 8 para Python
- Documentar funciones y clases
- Escribir tests para nuevas funcionalidades
- Mantener cobertura de tests
- Usar el sistema de Issues de GitHub
- Incluir pasos para reproducir el bug
- Especificar versiones de Python y Django
- Adjuntar capturas de pantalla si es necesario
Este proyecto está bajo la Licencia MIT. Ver el archivo LICENSE para más detalles.
Douglas Suárez Zamorano
- GitHub: @darksea48
- Email: d.suarez.zamorano@gmail.com
- Cynthia Castillo y Ricardo Vega: Mis profesores del Bootcamp de Python/Django
- Valeria Jara Bugueño: Mi amada esposa que me ha estado acompañando en todo este trayecto en este curso
- Cristian Astudillo y Gerard Bourguett: Mis grandes amigos que me han apoyado en todo
- Joaquín González: Mi compañero de trabajo que me ha acompañado en todo este curso con buenos tips de Python
⭐ Si este proyecto te ha sido útil, ¡considera darle una estrella en GitHub!
📚 Proyecto desarrollado como parte del Bootcamp Full Stack Python/Django