Archivos media en Django

Intermedio
Django
Django
Actualizado: 18/04/2026

Configuración de archivos media

Los archivos media son archivos subidos por los usuarios: imágenes de perfil, documentos adjuntos, vídeos… A diferencia de los archivos estáticos (que son parte del código), los archivos media se almacenan fuera del repositorio y se gestionan en tiempo de ejecución.

# settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

Diagrama conceptual de Archivos media en Django

  • MEDIA_URL: prefijo de URL para acceder a los archivos subidos.
  • MEDIA_ROOT: ruta absoluta en el sistema de archivos donde se almacenan.

Servir archivos media en desarrollo

Durante el desarrollo, Django puede servir los archivos media añadiendo una URL especial en urls.py del proyecto:

# urls.py del proyecto
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    # ... tus URLs ...
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

En producción, Nginx u otro servidor web se encarga de servir los archivos media directamente, sin pasar por Django.

Modelos con FileField e ImageField

# models.py
from django.db import models

def ruta_avatar(instance, filename):
    """Genera la ruta de almacenamiento dinámica."""
    ext = filename.split('.')[-1]
    return f'avatares/{instance.username}.{ext}'

class Perfil(models.Model):
    usuario = models.OneToOneField('auth.User', on_delete=models.CASCADE)
    avatar = models.ImageField(
        upload_to='avatares/',   # Subdirectorio dentro de MEDIA_ROOT
        blank=True,
        null=True,
        help_text='Imagen de perfil (máx. 2 MB)'
    )
    curriculum = models.FileField(
        upload_to='curriculos/%Y/%m/',  # Organiza por año/mes
        blank=True,
        null=True
    )

    def __str__(self):
        return f'Perfil de {self.usuario.username}'

class Producto(models.Model):
    nombre = models.CharField(max_length=100)
    imagen_principal = models.ImageField(upload_to='productos/', blank=True)
    imagen_miniatura = models.ImageField(upload_to='productos/miniaturas/', blank=True)

    def get_imagen_url(self):
        """Devuelve la URL de la imagen o una imagen por defecto."""
        if self.imagen_principal:
            return self.imagen_principal.url
        return '/static/img/producto_default.jpg'

Para usar ImageField, es necesario instalar Pillow:

pip install Pillow

Formularios con subida de archivos

Los formularios que incluyen FileField o ImageField requieren enctype="multipart/form-data" en la etiqueta <form>:

<form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Guardar</button>
</form>

En la vista, los archivos se reciben en request.FILES:

# views.py
from django.shortcuts import render, redirect
from .forms import PerfilForm

def editar_perfil(request):
    perfil = request.user.perfil
    if request.method == 'POST':
        form = PerfilForm(request.POST, request.FILES, instance=perfil)
        if form.is_valid():
            form.save()
            return redirect('mi-perfil')
    else:
        form = PerfilForm(instance=perfil)
    return render(request, 'usuarios/editar_perfil.html', {'form': form})

Validar archivos en formularios

# forms.py
from django import forms
from django.core.exceptions import ValidationError
from .models import Perfil

class PerfilForm(forms.ModelForm):
    class Meta:
        model = Perfil
        fields = ['avatar', 'curriculum']

    def clean_avatar(self):
        avatar = self.cleaned_data.get('avatar')
        if avatar:
            # Validar tamaño máximo (2 MB)
            if avatar.size > 2 * 1024 * 1024:
                raise ValidationError('La imagen no puede superar los 2 MB.')
            # Validar extensión
            extension = avatar.name.split('.')[-1].lower()
            if extension not in ['jpg', 'jpeg', 'png', 'webp']:
                raise ValidationError('Formato no permitido. Usa JPG, PNG o WebP.')
        return avatar

Acceder a archivos en plantillas

{% if perfil.avatar %}
    <img src="{{ perfil.avatar.url }}" alt="Avatar de {{ perfil.usuario.username }}">
    <p>Tamaño: {{ perfil.avatar.size|filesizeformat }}</p>
{% else %}
    <img src="{% static 'img/avatar_default.png' %}" alt="Avatar por defecto">
{% endif %}

{% if perfil.curriculum %}
    <a href="{{ perfil.curriculum.url }}" download>Descargar CV</a>
{% endif %}

El atributo .url del campo genera la URL completa usando MEDIA_URL + la ruta almacenada en la base de datos.

Eliminar archivos al borrar el modelo

Django no elimina automáticamente los archivos del disco cuando se borra un registro. Para hacerlo, usa señales:

from django.db.models.signals import post_delete
from django.dispatch import receiver
import os

@receiver(post_delete, sender=Perfil)
def eliminar_avatar(sender, instance, **kwargs):
    if instance.avatar:
        if os.path.isfile(instance.avatar.path):
            os.remove(instance.avatar.path)
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Django es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Django

Explora más contenido relacionado con Django y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Configurar MEDIA_URL y MEDIA_ROOT para gestionar archivos subidos por usuarios. Definir modelos con FileField e ImageField para almacenar rutas de archivos. Configurar las URLs de media en desarrollo para servir los archivos. Procesar formularios con archivos usando request.FILES y enctype multipart. Gestionar el almacenamiento de archivos con parámetros upload_to y custom storage.