Upload dei file
Dopo aver visto come gestire i file statici del nostro progetto ora andiamo a vedere come è possibile
gestire file dinamici caricati dagli utenti. In questa sezione vogliamo estendere il modello Corso
per poter caricare un pdf contenente la brochure del corso.
Per prima cosa andiamo a creare nella directory principale del progetto la directory che conterrà i file caricati:
mkdir uploads
Quindi andiamo a configurare in catalogo/settings.py
il path alla directory che abbiamo appena
creato come MEDIA_ROOT
ed il path da prefissare nelle url dai quali servire i file caricati come
MEDIA_URL
:
MEDIA_ROOT = BASE_DIR / 'uploads'
MEDIA_URL = '/media/'
Per poter accedere ai file caricati in sviluppo possiamo usare l'helper static
che servirà i file
presenti in MEDIA_ROOT
alle url prefissate da MEDIA_URL
aggiungendolo alle url in
catalogo/urls.py
:
from django.conf import settings
from django.conf.urls.static import static
...
urlpatterns = [
...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
static
funziona esclusivamente quando DEBUG
è attivato nella nostra configurazione.
Una volta configurato il nostro progetto per gestire i file caricati dagli utenti andiamo a modificare
il modello Corso
aggiungendo il nuovo campo brochure
di tipo FileField
in corsi/models.py
:
class Corso(models.Model):
titolo = models.CharField(max_length=100)
descrizione = models.TextField()
categoria = models.ForeignKey(Categoria, null=True, blank=True, on_delete=models.PROTECT)
docenti = models.ManyToManyField(User)
brochure = models.FileField(upload_to="corsi/corso/brochure/", null=True, blank=True)
creato = models.DateTimeField(auto_now_add=True)
aggiornato = models.DateTimeField(auto_now=True)
def get_absolute_url(self):
return reverse("corsi-detail", args=[self.pk])
def __str__(self):
return self.titolo
class Meta:
verbose_name_plural = "Corsi"
Per il campo brochure
andiamo a configurare il parametro upload_to
per specificare il path dentro
a MEDIA_ROOT
nel quale salvare i file caricati; abbiamo usato corsi/corso/brochure
per identificare
rispettivamente l'applicazione, il modello e quindi il campo dove abbiamo associato il file caricato.
Rendiamo il campo nullable perché lo stiamo aggiungendo quando abbiamo già delle istanze di Corso in
database.
Quindi creiamo la migrazione ed applichiamola:
python3 manage.py makemigrations
python3 manage.py migrate
Il prossimo passo consiste nell'aggiornare il form del corso. Aggiorniamo CorsoForm
in
corsi/forms.py
:
class CorsoForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = "corso-form"
self.helper.form_method = "post"
self.helper.form_action = ""
self.helper.attrs = {"enctype": "multipart/form-data"}
self.helper.add_input(Submit('submit', 'Salva'))
class Meta:
model = Corso
fields = ["titolo", "descrizione", "categoria", "docenti", "brochure"]
Abbiamo modificato il form per permettere di modificare il nuovo campo brochure
aggiungendolo in
fields
e abbiamo aggiunto tramite l'attributo attrs
dell'istanza di FormHelper
l'attributo
enctype
con valore multipart/form-data
in modo da permettere l'upload di file in aggiunti ai normali
campi del form.
Dopo il form aggiorniamo anche il template di dettaglio del corso per visualizzare il valore del nuovo
campo brochure
aggiornando il file corsi/templates/corsi/corso_detail.html
:
{% extends "corsi/base.html" %}
{% block content %}
<h2>Corso: {{ corso }}</h2>
<p><a href="{% url 'corsi-update' corso.pk %}">Modifica</a></p>
<p>Categoria: {{ corso.categoria }}</p>
<p>Docenti: {% for docente in corso.docenti.all %}{{ docente.username }}{% endfor %}</p>
<p>Descrizione: {{ corso.descrizione }}</p>
<p>Brochure: {% if corso.brochure %}
<a href="{{ corso.brochure.url }}">Scarica</a>
{% else %}
Nessuna brochure disponibile
{% endif %}</p>
<p><a href="{% url 'corsi-list' %}">Torna alla lista</a></p>
{% endblock %}
In questo caso stiamo mostrando un link al file quando presente o in alternativa un messaggio di assenza
del file. Facciamo attenzione al fatto che viene valutata la presenza del file tramite l'attributo
brochure
mentre l'url è disponibile come attributo url
solo quando il campo ha un file associato.
Possiamo verificare di poter caricare una brochure puntando il nostro browser all'indirizzo http://127.0.0.1:8000/corsi/corsi e aggiungendo od aggiornando un corso.
Dal momento che i file caricati dagli utenti non devono essere salvati in git, aggiungiamo la directory
uploads
ai file che git deve ignorare:
echo "uploads/" >> .gitignore
Infine aggiorniamo i nostri progressi su git e GitHub:
git add corsi catalogo .gitignore
git commit -m "Aggiungiamo il campo brochure a Corso"
git push origin main
Esercizi
Leggi la documentazione delle configurazioni MEDIA_ROOT e MEDIA_URL.
Leggi la reference del campo FileField e un articolo specifico sulla gestione dei file in Django.
Consulta la documentazione per scoprire come è implementato l'upload dei file.