Plataforma colaborativa de recetas con sistema de roles jerárquicos y experiencia de usuario tipo revista gastronómica.
Proyecto final del curso Desarrollo Web — Informatorio 2024.
Evolucionado con diseño profesional, dark mode, animaciones, pruebas automatizadas y despliegue en producción con Render.
- Demo en Vivo
- Stack Tecnológico
- Features
- Arquitectura del Proyecto
- Modelo de Datos
- Sistema de Roles y Permisos
- Quick Start
- Entorno de Desarrollo
- Pruebas
- Deploy en Render
- Pipeline de Build
- Mejoras Aplicadas
- Roadmap
- Contribución
- Licencia
La aplicación está desplegada en Render (plan free) con PostgreSQL:
| Usuario | Contraseña | Rol |
|---|---|---|
admin |
admin123 |
👑 Administrador |
colaborador |
colab123 |
✍️ Colaborador |
miembro1 |
miembro123 |
👤 Miembro |
⚠️ El plan free de Render "duerme" tras 15 min sin actividad. La primera carga puede demorar ~30 segundos.
| Capa | Tecnología | Versión |
|---|---|---|
| Backend | Django | 4.2.3 |
| Lenguaje | Python | 3.11.15 |
| Base de datos | PostgreSQL (prod) / SQLite (dev) | — |
| Servidor ASGI/WSGI | Gunicorn + gthread | 22.0 |
| Static/Media | WhiteNoise | 6.6 |
| Frontend | Bootstrap 5 + CSS Custom Properties | 5.2.1 |
| Tipografía | Playfair Display + Inter | — |
| Iconos | Bootstrap Icons | 1.11 |
| Testing | pytest + factory-boy + coverage | 7.4 / 3.3 / 7.2 |
| Imágenes | Pillow + Unsplash (fotos reales) | 10.0 |
| Auth | django.contrib.auth + Custom User Model | — |
| ORM | Django ORM (PostgreSQL compatible) | — |
| Deploy | Render (web service + PostgreSQL) | — |
- Registro y autenticación con validación de formularios cliente + servidor
- Tres roles jerárquicos: Miembro, Colaborador, Administrador
- Perfil con avatar personalizable
- Sincronización automática de grupos mediante Django Signals
- CRUD completo de artículos con imágenes
- Categorías para organización temática
- Sistema de comentarios con permisos por rol
- Editor de contenido con formularios validados
- Diseño revista gastronómica inspirado en blogs profesionales
- Dark mode con persistencia en localStorage
- Animaciones scroll reveal con IntersectionObserver
- Responsive design mobile-first
- Toast notifications y feedback visual en formularios
- Hero full-width con artículo destacado
- CSRF protection + HTTPS forzado en producción
- Permission-based authorization (3 roles)
- Passwords hasheados con
set_password() - Settings separados por entorno (base/local/production)
- HSTS, XSS filter, Content-Type nosniff, X-Frame-Options DENY
- Proxy SSL header para Render
- Deploy automatizado vía Render + GitHub
- Build pipeline con seed data + imágenes Unsplash reales
- PostgreSQL gestionado por Render (free tier)
- WhiteNoise para servir static + media sin CDN externo
- Logging a stdout para seguimiento en Render Dashboard
Blog_Cocina-Django_Python/
├── blog_cocina/ # Configuración principal
│ ├── settings/
│ │ ├── base.py # Settings compartidos
│ │ ├── local.py # Desarrollo (SQLite + debug)
│ │ └── production.py # Producción (PostgreSQL + HTTPS + WhiteNoise)
│ ├── urls.py # Routing principal
│ ├── views.py # Vistas principales (home, acerca_de)
│ └── context_processors.py # Contexto global (categorías en footer)
│
├── apps/
│ ├── usuarios/ # Autenticación y roles
│ │ ├── models.py # Custom User + permisos
│ │ ├── forms.py # RegistroForm
│ │ ├── views.py # Login / Logout / Registro
│ │ ├── urls.py
│ │ ├── admin.py
│ │ └── management/
│ │ └── commands/
│ │ └── seed_data.py # Poblado de BD con datos reales
│ │
│ ├── articulos/ # Core: artículos + comentarios
│ │ ├── models.py # Articulo, Categoria, Comentario
│ │ ├── forms.py # ArticuloForm, CategoriaForm, ComentarioForm
│ │ ├── views/ # Vistas modulares
│ │ │ ├── articulos.py
│ │ │ ├── categorias.py
│ │ │ └── comentarios.py
│ │ ├── urls.py
│ │ ├── admin.py
│ │ ├── management/commands/
│ │ │ └── fix_images.py # Forzar imágenes Unsplash reales en build
│ │ └── tests/ # Tests unitarios + integración
│ │ ├── factories.py
│ │ ├── conftest.py
│ │ ├── test_forms.py
│ │ ├── test_models.py
│ │ ├── test_integration.py
│ │ └── test_views/
│ │
│ └── contacto/ # Formulario de contacto
│ ├── models.py
│ ├── forms.py
│ ├── views.py
│ └── tests/
│
├── templates/ # Templates Django
│ ├── base.html # Layout principal (navbar, footer, dark mode)
│ ├── index.html # Home con hero + grid + newsletter
│ ├── acerca_de.html
│ ├── articulos/ # CRUD artículos
│ │ ├── listarArticulos.html
│ │ ├── detalleArticulos.html
│ │ ├── addArticulo.html
│ │ └── edit_articulo.html
│ ├── categorias/
│ ├── contacto/
│ └── usuarios/
│
├── static/
│ ├── css/styles.css # ~22KB de CSS custom optimizado
│ └── js/scripts.js # Dark mode, scroll reveal, validación frontend
│
├── media/articulos/ # Imágenes reales de comida (Unsplash, 22–160KB c/u)
├── render.yaml # Configuración de deploy en Render
├── build.sh # Script de build para Render
├── .env # Variables de entorno (local)
├── pytest.ini # Configuración de pytest
├── requirements.txt
└── README.md
erDiagram
Usuario ||--o{ Articulo : "es autor"
Usuario ||--o{ Comentario : "escribe"
Categoria ||--o{ Articulo : "clasifica"
Articulo ||--o{ Comentario : "tiene"
Usuario {
int id PK
string username
string email
string password
string tipo_usuario "Miembro|Colaborador|Administrador"
image imagen
bool is_staff
bool is_superuser
}
Articulo {
int id PK
string titulo
text contenido_breve
text contenido_completo
datetime fecha_publicacion
image imagen
int categoria_articulo FK
int usuario_articulo FK
}
Categoria {
int id PK
string descripcion
}
Comentario {
int id PK
int articulo_comentario FK
int usuario_comentario FK
text comentario
datetime fecha_publicacion
}
Contacto {
int id PK
string nombre
string email
string telefono
text mensaje
datetime fecha
}
| Rol | Publicar | Editar propios | Editar ajenos | Eliminar | Comentar | Admin panel |
|---|---|---|---|---|---|---|
| Miembro | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| Colaborador | ✅ | ✅ | ❌ | ✅ propios | ✅ | ❌ |
| Administrador | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Implementado mediante grupos Django + un campo tipo_usuario sincronizado por signals (post_save).
python >= 3.11
pip / uv# 1. Clonar
git clone https://github.com/Gerardo-Rioss/Blog_Cocina-Django_Python.git
cd Blog_Cocina-Django_Python
# 2. Entorno virtual
python -m venv .venv
source .venv/Scripts/activate # Windows
# source .venv/bin/activate # Linux/Mac
# 3. Dependencias
pip install -r requirements.txt
# 4. Variables de entorno
echo "SECRET_KEY=django-insecure-dev-key-$(openssl rand -hex 32)" > .env
# 5. Migraciones
python manage.py migrate --settings=blog_cocina.settings.local
# 6. Datos de prueba (opcional)
python manage.py seed_data --settings=blog_cocina.settings.local
# 7. Iniciar servidor
python manage.py runserver --settings=blog_cocina.settings.local| Usuario | Contraseña | Rol |
|---|---|---|
admin |
admin123 |
👑 Administrador |
colaborador |
colab123 |
✍️ Colaborador |
chef_maria |
chef123 |
✍️ Colaborador |
cocinero_juan |
cocina123 |
✍️ Colaborador |
miembro1 |
miembro123 |
👤 Miembro |
lector_ana |
lector123 |
👤 Miembro |
El archivo blog_cocina/settings/local.py ya está configurado para usar SQLite automáticamente. No requiere instalar PostgreSQL.
# local.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}# Regenerar BD desde cero
rm db.sqlite3 media/articulos/*.jpg
python manage.py migrate --settings=blog_cocina.settings.local
python manage.py seed_data --settings=blog_cocina.settings.localEl comando seed_data descarga/puebla:
- 10 categorías gastronómicas
- 6 usuarios con distintos roles
- 20 artículos con imágenes reales de Unsplash (800×500px)
- 30 comentarios distribuidos aleatoriamente
- 5 mensajes de contacto
pytest --ds=blog_cocina.settings.local -vpytest --ds=blog_cocina.settings.local --cov=apps --cov-report=term-missing✅ 146 passed, 0 failed, 0 errors
Tests incluidos:
- Models: creación, validación, constraints, relaciones, helpers de permisos
- Forms: validación de campos requeridos, formatos inválidos, edge cases
- Views: status codes, templates, redirects, permisos por rol
- Integration: flujo completo registro → login → CRUD → comentarios
- Signals: sincronización automática de grupos Django
El proyecto está configurado para deploy en Render con PostgreSQL y build automatizado.
services:
- type: web
name: blog-cocina
runtime: python
region: ohio
plan: free
branch: proyecto_final
buildCommand: "./build.sh"
startCommand: "gunicorn blog_cocina.wsgi:application --workers=2 --threads=4 --worker-class=gthread --timeout=120"
envVars:
- key: DJANGO_SETTINGS_MODULE
value: blog_cocina.settings.production
- key: SECRET_KEY
generateValue: true
- key: ALLOWED_HOSTS
value: ".onrender.com,localhost"
- key: DATABASE_URL
fromDatabase:
name: blog-cocina-db
property: connectionString
databases:
- name: blog-cocina-db
region: ohio
plan: free
databaseName: blog_cocina
user: blog_cocina_user| Aspecto | Implementación |
|---|---|
| Settings | production.py hereda de base.py |
| DB | dj-database-url lee DATABASE_URL de Render |
| Static | WhiteNoise con cacheo de archivos |
| Media | Copiadas a staticfiles/ durante el build, servidas por WhiteNoise |
| HTTPS | Forzado vía SECURE_SSL_REDIRECT + proxy header |
| HSTS | 1 año con subdominios y preload |
| Logging | StreamHandler a stdout para Render Dashboard |
El archivo build.sh ejecuta en cada deploy:
1. pip install -r requirements.txt
2. python manage.py collectstatic --noinput --clear
3. Copia imágenes de media/ a staticfiles/
4. python manage.py migrate --noinput
5. python manage.py seed_data → Puebla BD con datos de prueba
6. python manage.py fix_images → Asigna imágenes Unsplash reales
⚡ El comando
fix_imagesreemplaza imágenes placeholder por fotos reales de comida (Unsplash, 22–160KB c/u), asegurando un aspecto profesional desde el primer deploy.
| Área | Antes | Después |
|---|---|---|
| CSS | 250KB (Bootstrap duplicado) | ~22KB de CSS custom optimizado |
| JS | Vacío | Dark mode, scroll reveal, toasts, validación frontend |
| Templates | HTML mal formado, botones anidados | Semántica limpia, diseño revista profesional |
| Navbar | Fondo oscuro genérico | Navbar blur con indicador activo |
| Footer | "Your Website 2023" | Footer completo con redes, catálogo, navegación |
| Hero | Estático | Dinámico con artículo destacado + overlay |
| Comentarios | <button><a> anidados |
Sistema moderno tipo card con acciones |
| Dark mode | ❌ No existía | ✅ Toggle con persistencia y preferencia del sistema |
| Imágenes | Placeholder 1×1 | 20 fotos reales de Unsplash (22–160KB) |
| Formularios | form.as_table |
Cards estilizadas con feedback visual |
| Login/Registro | Básico | Cards centradas con iconografía |
| Área | Antes | Después |
|---|---|---|
| Tests | 8 errores pre-existentes | 146 tests, 0 fallos |
| Factory Usuario | No funcionaba | _create override con grupos exclusivos |
| Settings | Single file | Base + Local + Production separados |
| Migrations | Desincronizadas (contacto) | Sync completas |
| Área | Antes | Después |
|---|---|---|
| Deploy | Manual / sin configurar | Render + GitHub automatizado |
| Build | Inexistente | build.sh con migraciones + seed + imágenes |
| Base de datos | SQLite solamente | PostgreSQL en producción |
| Media en prod | No servidas | Copiadas a staticfiles/ + WhiteNoise |
| Imágenes reales | Placeholder genérico | fix_images con 20 fotos Unsplash reales |
| HTTPS | ❌ | ✅ Forzado con HSTS 1 año |
| Seguridad | ❌ | HSTS, XSS filter, Content-Type nosniff, X-Frame-Options |
- Dark mode completo
- Diseño revista profesional
- Imágenes reales de comida (Unsplash)
- Sistema de roles funcional (3 niveles)
- Tests automatizados (146 ✅)
- Deploy en Render con PostgreSQL
- Build pipeline con seed data + fix_images
- Configuración de producción hardening (HTTPS, HSTS, SecurityMiddleware)
- Servicio de media vía WhiteNoise (sin CDN externo)
- Buscador full-text de artículos
- Paginación en listado
- Tags/etiquetas en artículos
- Perfil de usuario público
- Editor WYSIWYG (rich text)
- WebP/AVIF para imágenes optimizadas
- Docker + docker-compose (para desarrollo local)
- API REST (DRF)
- Social login (Google, GitHub)
- Recetas favoritas / bookmarks
- Notificaciones push
- Reportes de contenido inapropiado
- SEO optimizado (Open Graph + Schema.org)
- CI/CD con GitHub Actions
- CDN para media
- Fork el proyecto
- Creá tu feature branch (
git checkout -b feature/nueva-funcionalidad) - Hacé commit de tus cambios (
git commit -m 'feat: agregar X') - Push al branch (
git push origin feature/nueva-funcionalidad) - Abrí un Pull Request
Convenciones de commits: Conventional Commits (feat:, fix:, refactor:, test:, docs:)
Este proyecto fue desarrollado como trabajo final del Informatorio 2024 — Desarrollo Web con Django y Python.
GitHub · Portfolio · LinkedIn
🌐 Ver Demo en Vivo