Maintenant que nous avons vu comment créer plusieurs instances de modèles en envoyant un seul formulaire, voyons comment inclure différents formulaires, par le biais d’envois séparés, sur la même page.
Pour démontrer ceci, nous allons construire une page depuis laquelle nous modifierons et supprimerons un billet de blog.
Étape 1 : Créez les formulaires
Pour supprimer l’instance de Blog
, créez un nouveau formulaire, DeleteBlogForm
. Pour modifier l’instance de Blog
, utilisez le BlogForm
déjà créé.
Néanmoins, il nous faut un moyen de distinguer ces deux formulaires lorsque nous récupérons les données de la requête POST
dans la vue.
Pour atteindre ce but, attachez un champ caché à chaque formulaire. Voici comment faire :
# blog/forms.py
class BlogForm(forms.ModelForm):
edit_blog = forms.BooleanField(widget=forms.HiddenInput, initial=True)
class Meta:
model = models.Blog
fields = ['title', 'content']
class DeleteBlogForm(forms.Form):
delete_blog = forms.BooleanField(widget=forms.HiddenInput, initial=True)
Vous avez attaché un champ edit_blog
et un champ delete_blog
aux formulaires BlogForm
et DeleteBlogForm
, respectivement. Ces champs utilisent le widget HiddenInput
, et ne seront pas vus par l’utilisateur sur le front-end. Le choix du type de champ et de la valeur initiale est quelque peu arbitraire, vu que nous allons simplement vérifier la présence du champ dans la vue.
Étape 2 : Incluez les formulaires dans une vue
Maintenant que nous avons les formulaires, gérons-les dans une vue.
Premièrement, créez une vue edit_blog
sans gestion de requête POST
.
# blog/views.py
@login_required
def edit_blog(request, blog_id):
blog = get_object_or_404(models.Blog, id=blog_id)
edit_form = forms.BlogForm(instance=blog)
delete_form = forms.DeleteBlogForm()
if request.method == 'POST':
pass
context = {
'edit_form': edit_form,
'delete_form': delete_form,
}
return render(request, 'blog/edit_blog.html', context=context)
Depuis que vous avez ajouté les champs cachés aux formulaires, vous pouvez vérifier quel formulaire est envoyé en vérifiant la présence de ce champ dans les données POST
. Puis, vous pouvez simplement gérer le formulaire comme d’habitude.
@login_required
def edit_blog(request, blog_id):
blog = get_object_or_404(models.Blog, id=blog_id)
edit_form = forms.BlogForm(instance=blog)
delete_form = forms.DeleteBlogForm()
if request.method == 'POST':
if 'edit_blog' in request.POST:
edit_form = forms.BlogForm(request.POST, instance=blog)
if edit_form.is_valid():
edit_form.save()
return redirect('home')
if 'delete_blog' in request.POST:
delete_form = forms.DeleteBlogForm(request.POST)
if delete_form.is_valid():
blog.delete()
return redirect('home')
context = {
'edit_form': edit_form,
'delete_form': delete_form,
}
return render(request, 'blog/edit_blog.html', context=context)
Étape 3 : Ajoutez le gabarit
Maintenant que la vue est construite, travaillons sur le gabarit :
# blog/templates/blog/edit_blog.html
{% extends 'base.html' %}
{% block content %}
<h2>Modifier le billet</h2>
<form method="post">
{{ edit_form.as_p }}
{% csrf_token %}
<button type="submit" >Sauvegarder</button>
</form>
<hr>
<h3>Supprimer le billet ?</h3>
<form method="post">
{{ delete_form }}
{% csrf_token %}
<button type="submit" >Supprimer</button>
</form>
{% endblock content %}
L’inclusion des champs cachés garantit que chaque formulaire n’est géré que lorsque vous avez l’intention de l’envoyer. Vous pouvez désormais inclure n’importe quelle quantité de formulaires sur la même page.
Étape 4 : Ajoutez le modèle d’URL
Ajoutez maintenant le modèle d’URL pour la page de modification du billet.
# fotoblog/urls.py
urlpatterns = [
…
path('blog/<int:blog_id>/edit', blog.views.edit_blog, name='edit_blog'),
]
Étape 5 : Ajoutez un lien vers la page de modification du billet
Et pour finir, ajoutez un lien vers la page de modification du billet depuis la page de visualisation du billet.
# blog/templates/blog/view_blog.html
{% extends 'base.html' %}
{% block content %}
<h2>{{ blog.title }}</h2>
<p><a href="{% url 'edit_blog' blog.id %}">Modifier le billet</a></p>
<img src="{{ blog.photo.image.url }}">
<p>{{ blog.photo.caption }}</p>
<p>{{ blog.content }}</p>
{% endblock content %}
Et voilà, nous avons fini ! Voyons à quoi ressemble la page de modification du billet :
Elle a l’air très bien ! Vous avez remarqué les deux boutons « Sauvegarder » et « Supprimer » séparés sur la page ? Ces deux boutons vont envoyer nos deux formulaires différents. Amusez-vous à tester cette fonctionnalité !
Incluez plusieurs instances du même formulaire avec des ensembles de formulaires, ou formsets
Vous possédez maintenant deux techniques pour inclure différents formulaires sur la même page. Vous pouvez les envoyer simultanément ou séparément. Mais que se passerait-il si vous vouliez inclure le même formulaire plusieurs fois sur une même page ?
Vous pouvez utiliser des ensembles de formulaires, ou formsets. Découvrez dans la vidéo ci-dessous comment créer une page qui permettra à un utilisateur de charger plusieurs photos en même temps.
Étape 1 : Créez un formset dans une vue avec formset_factory
Vous avez déjà PhotoForm
qui peut instancier le modèle Photo
. Vous pouvez réutiliser ce formulaire plutôt que d’en créer un nouveau.
Pour générer un formset avec ce formulaire, utilisez la méthodeformset_factory
que vous trouverez dans django.forms
. Cette méthode prend la classe formulaire (« form ») comme premier argument, puis un autre argument,extra
, qui définit combien d’instances du formulaire vous voulez faire générer. Cette fonction « factory » (usine) renvoie une classe, que vous devrez ensuite instancier dans la vue.
Concrètement, voici de quoi ça a l’air :
# blog/views.py
from django.forms import formset_factory
@login_required
def create_multiple_photos(request):
PhotoFormSet = formset_factory(forms.PhotoForm, extra=5)
formset = PhotoFormSet()
if request.method == 'POST':
pass
return render(request, 'blog/create_multiple_photos.html', {'formset': formset})
Pour traiter les données POST
, vous pouvez exécuter la méthode is_valid()
sur le formset
, exactement comme vous pouvez le faire sur un formulaire. Puis, vous pouvez itérer à travers les formulaires dans le formset en utilisant une boucle. Vous pouvez les gérer comme vous le feriez habituellement. Leformset
ne sera pas invalidé si des formulaires individuels sont vides, alors vérifions si un formulaire contient des données avant de le sauvegarder, comme ceci :
# blog/views.py
@login_required
def create_multiple_photos(request):
PhotoFormSet = formset_factory(forms.PhotoForm, extra=5)
formset = PhotoFormSet()
if request.method == 'POST':
formset = PhotoFormSet(request.POST, request.FILES)
if formset.is_valid():
for form in formset:
if form.cleaned_data:
photo = form.save(commit=False)
photo.uploader = request.user
photo.save()
return redirect('home')
return render(request, 'blog/create_multiple_photos.html', {'formset': formset})
Ceci sauvegardera tous les formulaires, avant de nous rediriger vers la page d’accueil. Et maintenant, ajoutons un gabarit.
Étape 2 : Créez le gabarit
Il y a deux méthodes pour inclure un formset dans un gabarit. La plus simple : inclure le formset comme vous le feriez pour un formulaire.
# blog/templates/blog/create_multiple_photos.html
{% extends 'base.html' %}
{% block content %}
<h2>Télécharger plusieurs photos</h2>
<form method="post" enctype="multipart/form-data">
{{ formset.as_p }}
{% csrf_token %}
<button type="submit" >Publier</button>
</form>
{% endblock content %}
Vous voudrez peut-être présenter chaque formulaire du formset individuellement. Pour ce faire, n’oubliez pas d’inclure {{ formset.management_form }}
dans les balises <form>
. Le management_form
n’inclut rien qui sera vu par l’utilisateur sur le front-end, mais il comprend certaines métadonnées dont Django a besoin pour traiter le formset, par exemple le nombre de formulaires dans le formset.
Pour inclure un formset dans une page tout en gérant l’affichage de chaque formulaire, vous pouvez ajouter :
# blog/templates/blog/create_multiple_photos.html
{% extends 'base.html' %}
{% block content %}
<h2>Télécharger des photos</h2>
<form method="post" enctype="multipart/form-data">
{{ formset.management_form }}
{% csrf_token %}
{% for form in formset %}
{{ form.as_p }}
{% endfor %}
<button type="submit" >Publier</button>
</form>
{% endblock content %}
Étape 3 : Ajoutez le modèle d’URL
À nouveau, vous devez ajouter le modèle d’URL.
# fotoblog/urls.py
urlpatterns = [
…
path('photo/upload-multiple/', blog.views.create_multiple_photos,
name='create_multiple_photos'),
]
Étape 4 : Ajoutez un lien dans la barre latérale
Et un lien dans la barre latérale.
# templates/base.html
...
<p><a href="{% url 'photo_upload' %}">Télécharger une photo</a></p>
<p><a href="{% url 'create_multiple_photos' %}">Télécharger plusieurs photos</a></p>
<p><a href="{% url 'upload_profile_photo' %}">Changer la photo de profil</a></p>
...
Et c’est tout ! Vous pouvez maintenant inclure plusieurs instances du même formulaire sur la même page. Voyons de quoi ça a l’air :
Testez cette possibilité, avant de me retrouver au prochain chapitre. Nous y verrons comment surcharger des méthodes de modèle.
En résumé
Vous pouvez gérer plusieurs formulaires séparés sur une seule page, en incluant un champ caché qui identifie le formulaire envoyé.
Vous pouvez utiliser des ensembles de formulaires, dits formsets, pour créer plusieurs instances différentes du même formulaire sur une seule page.
Maintenant que vous savez gérer plusieurs formulaires dans une seule vue, vous pouvez apprendre à étendre des modèles avec des méthodes personnalisées !