Este es un sistema CRM (Customer Relationship Management) desarrollado en Django que permite gestionar clientes, reportes y archivos asociados. 1
- Gestión de Clientes: Crear, leer, actualizar y eliminar clientes 2
- Gestión de Reportes: Administrar reportes asociados a clientes 3
- Subida de Archivos: Adjuntar archivos a los reportes 4
- Panel de Administración: Interface administrativa completa 5
- Búsqueda y Filtrado: Funcionalidad de búsqueda para clientes y reportes 6
- Python 3.8+
- Django 4.0+
- SQLite (incluido con Python)
# Crear entorno virtual
python -m venv .venv
# Activar entorno virtual
# En Windows:
.venv\Scripts\activate
# En macOS/Linux:
source .venv/bin/activate# Instalar Django
pip install django
# Si tienes un archivo requirements.txt (crear uno con):
pip freeze > requirements.txtEl proyecto utiliza la siguiente configuración: 7
La configuración de base de datos utiliza SQLite: 8
# Crear migraciones
python manage.py makemigrations
# Aplicar migraciones
python manage.py migratepython manage.py createsuperuserpython manage.py runserverEl servidor estará disponible en http://127.0.0.1:8000/ 9
CRM_DEMO-Django/
├── manage.py
├── crm_web/
│ ├── settings.py
│ ├── urls.py
│ ├── wsgi.py
│ └── asgi.py
├── apps/
│ └── clients/
│ ├── models.py
│ ├── views.py
│ ├── urls.py
│ ├── admin.py
│ ├── apps.py
│ ├── tests.py
│ └── migrations/
└── templates/
├── base.html
├── clients/
└── reports/
name: Nombre completo del clienteemail: Email único por clientephone: Teléfono opcional 2
client: Relación con Clientetitle: Título del reportedescription: Descripción opcionalcreated_at: Fecha de creación 3
report: Relación con Reportefile: Archivo subidouploaded_at: Fecha de subida 4
/clients/- Lista de clientes/clients/add/- Crear cliente/clients/<id>/- Detalle de cliente/clients/<id>/edit/- Editar cliente/clients/<id>/delete/- Eliminar cliente
/reports/- Lista de reportes/reports/add/- Crear reporte/reports/<id>/- Detalle de reporte/reports/<id>/edit/- Editar reporte/reports/<id>/delete/- Eliminar reporte 10
El panel de administración está disponible en /admin/ y incluye:
- Gestión completa de clientes con búsqueda 11
- Gestión de reportes con archivos inline 5
- Subida de archivos directamente desde el reporte 12
Tanto clientes como reportes incluyen paginación de 10 elementos por página 15
El proyecto está configurado para español chileno: 16
Los archivos subidos se guardan en la carpeta media/report_files/ 17
La configuración de archivos media está en: 18
# Ejecutar el servidor de desarrollo
python manage.py runserver
# Crear migraciones después de cambios en modelos
python manage.py makemigrations
# Aplicar migraciones
python manage.py migrate
# Acceder al shell de Django
python manage.py shell
# Crear superusuario para admin
python manage.py createsuperuser
# Recopilar archivos estáticos (para producción)
python manage.py collectstaticEl proyecto utiliza vistas basadas en clases (CBV) para operaciones CRUD: 19
- Este es un proyecto de demostración con configuración de desarrollo
- La clave secreta está hardcodeada y debe cambiarse en producción 20
- Se utiliza SQLite para simplicidad, considerar PostgreSQL para producción
- Los archivos media se sirven en desarrollo automáticamente 21
File: crm_web/settings.py (L11-13)
SECRET_KEY = 'django-insecure-9yz5=bnr-i@b=i(a+h*@wer5evbp3-*l*&cpgmufks=h8^0416'
DEBUG = True
ALLOWED_HOSTS = ['127.0.0.1', 'localhost']File: crm_web/settings.py (L26-28)
# apps del proyecto
'apps.clients.apps.ClientsConfig',
]File: crm_web/settings.py (L75-80)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3', # base simple para práctica
'NAME': BASE_DIR / 'db.sqlite3',
}
}File: crm_web/settings.py (L95-99)
LANGUAGE_CODE = 'es-cl'
TIME_ZONE = 'America/Santiago'
USE_I18N = True
USE_L10N = True
USE_TZ = TrueFile: crm_web/settings.py (L108-109)
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'File: apps/clients/models.py (L7-14)
class Client(models.Model):
name = models.CharField(max_length=100) # Nombre completo del cliente
email = models.EmailField(unique=True) # Email único por cliente
phone = models.CharField(max_length=20, blank=True) # Teléfono opcional
def __str__(self):
# Esto se muestra en admin y relaciones
return self.nameFile: apps/clients/models.py (L20-32)
class Report(models.Model):
client = models.ForeignKey(
Client,
on_delete=models.CASCADE,
related_name='reports'
) # Cada report pertenece a un cliente. Si se borra el cliente, se borran sus reportes
title = models.CharField(max_length=150) # Título del reporte
description = models.TextField(blank=True) # Descripción opcional
created_at = models.DateTimeField(default=timezone.now) # Fecha de creación
def __str__(self):
# Nombre del reporte junto al cliente
return f"{self.title} - {self.client.name}"File: apps/clients/models.py (L38-48)
class ReportFile(models.Model):
report = models.ForeignKey(
Report,
on_delete=models.CASCADE,
related_name='files'
) # Cada archivo pertenece a un reporte. Si se borra el reporte, se borran sus archivos
file = models.FileField(upload_to='report_files/') # Carpeta dentro de MEDIA_ROOT
uploaded_at = models.DateTimeField(default=timezone.now) # Fecha de subida del archivo
def __str__(self):
return f"{self.file.name} ({self.report.title})"File: apps/clients/admin.py (L7-9)
class ReportFileInline(admin.TabularInline):
model = ReportFile
extra = 1 # Cuántas filas extra para subir archivosFile: apps/clients/admin.py (L14-16)
class ReportAdmin(admin.ModelAdmin):
list_display = ('title', 'client', 'created_at') # Columnas en la lista de reportes
inlines = [ReportFileInline] # Archivos relacionados se muestran inlineFile: apps/clients/admin.py (L21-23)
class ClientAdmin(admin.ModelAdmin):
list_display = ('name', 'email', 'phone') # Columnas en la lista de clientes
search_fields = ('name', 'email') # Permite buscar clientes por nombre o emailFile: apps/clients/views.py (L3-3)
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteViewFile: apps/clients/views.py (L16-16)
paginate_by = 10 # 10 clientes por páginaFile: apps/clients/views.py (L18-27)
def get_queryset(self):
"""Filtra clientes según parámetro de búsqueda 'q'"""
qs = super().get_queryset()
query = self.request.GET.get('q')
if query:
# Filtrar clientes cuyo nombre contenga el texto de búsqueda
qs = qs.filter(name__icontains=query)
return qsFile: apps/clients/views.py (L84-89)
if query:
# CORREGIDO: Buscar en título del reporte Y en nombre del cliente relacionado
qs = qs.filter(
Q(title__icontains=query) |
Q(client__name__icontains=query) # Acceder al nombre del cliente via relación
)File: apps/clients/urls.py (L7-21)
urlpatterns = [
# Clientes
path('clients/', views.ClientListView.as_view(), name='client_list'),
path('clients/<int:pk>/', views.ClientDetailView.as_view(), name='client_detail'),
path('clients/add/', views.ClientCreateView.as_view(), name='client_add'),
path('clients/<int:pk>/edit/', views.ClientUpdateView.as_view(), name='client_edit'),
path('clients/<int:pk>/delete/', views.ClientDeleteView.as_view(), name='client_delete'),
# Reportes
path('reports/', views.ReportListView.as_view(), name='report_list'),
path('reports/<int:pk>/', views.ReportDetailView.as_view(), name='report_detail'),
path('reports/add/', views.ReportCreateView.as_view(), name='report_add'),
path('reports/<int:pk>/edit/', views.ReportUpdateView.as_view(), name='report_edit'),
path('reports/<int:pk>/delete/', views.ReportDeleteView.as_view(), name='report_delete'),
]File: crm_web/urls.py (L12-14)
# Para servir archivos media en desarrollo
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)