Gestire l’upload di file con Django

Di seguito trovi una guida pratica e aggiornata per caricare file in modo sicuro ed efficiente usando ModelForm, gli upload handlers predefiniti e le impostazioni consigliate.

1) Configurazione di base: MEDIA_ROOT e MEDIA_URL

Django salva i file caricati usando lo storage predefinito (filesystem locale, a meno di override). Aggiungi in settings.py:

# settings.py

MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"

# Limiti consigliati

DATA_UPLOAD_MAX_MEMORY_SIZE = 10 * 1024 * 1024     # 10 MB per la request in memoria
FILE_UPLOAD_MAX_MEMORY_SIZE = 2 * 1024 * 1024      # 2 MB: sopra questo usa file temporanei su disco
FILE_UPLOAD_PERMISSIONS = 0o644                    # permessi sicuri per i file salvati 

2) URL di sviluppo per servire i media

In sviluppo (non in produzione), esponi MEDIA_URL aggiungendo in urls.py del progetto:

# urls.py (progetto)
from django.conf import settings
from django.conf.urls.static import static
from django.urls import path, include

urlpatterns = [
  path("", include("app.urls")),
]

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

3) Modello con FileField/ImageField

Definisci il campo e, se vuoi, personalizza la cartella di destinazione con upload_to (può essere una stringa o una callable per path dinamici):

# app/models.py
from django.db import models
from django.utils import timezone

def upload_to_docs(instance, filename):
  ts = timezone.now().strftime("%Y%m%d_%H%M%S")
  return f"uploads/docs/{ts}_{filename}"

class Document(models.Model):
  title = models.CharField(max_length=200)
  file = models.FileField(upload_to=upload_to_docs)  # o ImageField per immagini
  uploaded_at = models.DateTimeField(auto_now_add=True)

  def __str__(self):
    return self.title

4) ModelForm e validazioni

Usa un ModelForm e valida dimensione e tipo MIME in modo esplicito (mai fidarsi solo dell’estensione):

# app/forms.py
from django import forms
from .models import Document

ALLOWED_MIME = {"application/pdf", "text/plain"}
MAX_MB = 5

class DocumentForm(forms.ModelForm):
  class Meta:
    model = Document
    fields = ["title", "file"]

  def clean_file(self):
    f = self.cleaned_data["file"]
    # Dimensione
    if f.size > MAX_MB * 1024 * 1024:
        raise forms.ValidationError(f"Il file supera {MAX_MB} MB.")
    # Tipo MIME (usa content_type dell'UploadedFile)
    if getattr(f, "content_type", None) not in ALLOWED_MIME:
        raise forms.ValidationError("Tipo di file non consentito.")
    return f

5) View sincrona o asincrona

Le view classiche gestiscono request.FILES. In Django 5.2 puoi anche scrivere view async, ma la maggior parte dei casi va bene la vista standard:

# app/views.py
from django.shortcuts import render, redirect
from .forms import DocumentForm

def upload_view(request):
  if request.method == "POST":
    form = DocumentForm(request.POST, request.FILES)
    if form.is_valid():
      form.save()
      return redirect("upload_ok")
  else:
    form = DocumentForm()
    return render(request, "app/upload.html", {"form": form}) 

6) Template: multipart e csrf_token

Ricorda l’attributo enctype="multipart/form-data" sul form, altrimenti i file non vengono inviati:

<!-- app/templates/app/upload.html -->
<h1>Carica documento</h1>
<form method="post" enctype="multipart/form-data">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit">Carica</button>
</form>

7) Visualizzare e scaricare i file caricati

Dopo il salvataggio, il campo fornisce url e path:

<p>Titolo: {{ obj.title }}</p>
<p><a href="{{ obj.file.url }}" download>Scarica file</a></p>

8) Storage alternativi (S3, GCS, ecc.)

Per ambienti cloud usa un backend di storage compatibile (per esempio django-storages) e configura le credenziali. Il resto del codice applicativo resta invariato grazie all’API dello storage di Django.

9) Comprendere gli upload handlers

Di default Django usa due gestori: MemoryFileUploadHandler (per file piccoli, in RAM) e TemporaryFileUploadHandler (per file grandi, su disco temporaneo). Puoi sostituirli o aggiungerne di personalizzati con FILE_UPLOAD_HANDLERS se ti servono stream particolari o quote.

# settings.py (opzionale)
FILE_UPLOAD_HANDLERS = [
    "django.core.files.uploadhandler.MemoryFileUploadHandler",
    "django.core.files.uploadhandler.TemporaryFileUploadHandler",
]

10) Sicurezza: regole essenziali

  • Valida sempre dimensione e MIME; rinomina i file sul server (lo fa già l’esempio via upload_to).
  • Non eseguire mai file caricati (niente preview server-side di HTML/JS non sanificati).
  • Isola i media dal codice applicativo; in produzione fai servire i media dal web server o CDN, non da Django.
  • Per immagini, usa librerie che verificano i formati effettivi (es. Pillow) e considera una scansione antivirus su backend.

11) Esempio minimo completo: URL e pagina di esito

# app/urls.py
from django.urls import path
from .views import upload_view
from django.views.generic import TemplateView

urlpatterns = [
  path("upload/", upload_view, name="upload"),
  path("upload-ok/", TemplateView.as_view(template_name="app/ok.html"), name="upload_ok"),
] 
<!-- app/templates/app/ok.html -->
<h2>Upload completato!</h2>
<p>Il file è stato salvato correttamente.</p>

12) Check-list di produzione

  • Usa uno storage esterno (S3/GCS/Azure) con CDN.
  • Imposta CORS/headers corretti per i download.
  • Logga tentativi falliti e conserva metadati (IP, user agent) secondo le norme privacy.
  • Dimensiona DATA_UPLOAD_MAX_MEMORY_SIZE e FILE_UPLOAD_MAX_MEMORY_SIZE in base al carico.

Conclusione

Con Django l’upload è semplice grazie a ModelForm e agli upload handlers predefiniti che gestiscono in memoria i file piccoli e su disco quelli grandi. Aggiungi limiti, validazioni e uno storage adeguato per un flusso robusto e sicuro.

Torna su