• 12 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 22/03/2024

Ajoutez un service de création de billets de blog

Créez plusieurs modèles avec un seul formulaire

Maintenant que nous avons donné un moyen aux utilisateurs de téléverser des photos, donnons-leur la possibilité de créer aussi des billets de blog.

Parfois, en créant un formulaire, vous voudrez instancier plus d’un modèle à la fois. Dans notre cas, il faut que les utilisateurs puissent créer et une  Photo, et un post de  Blog  en même temps. Découvrez comment en vidéo ci-dessous.

Étape 1 : Créez les formulaires

Pour permettre aux utilisateurs d’instancier plusieurs modèles simultanément, il vous faut un formulaire pour chacun des modèles  Photo  et  Blog  .

Vous avez créé un  ModelForm  pour la  Photo  au chapitre précédent. Réutilisons-le ! Et maintenant, créons-en un pour le modèle  Blog  .

# blog/forms.py
from django import forms

from . import models


class PhotoForm(forms.ModelForm):
    ...


class BlogForm(forms.ModelForm):
    class Meta:
        model = models.Blog
        fields = ['title', 'content']

Étape 2 : Incluez les formulaires dans le gabarit

Voyons à présent comment inclure ces deux formulaires dans le gabarit. La vue n’a pas encore été créée, mais nommons déjà les formulaires  photo_form  et  blog_form  .

Nous voulons que les deux formulaires puissent être envoyés avec un clic unique sur un bouton, pour instancier les deux modèles simultanément. Pour ce faire, incluez ces deux formulaires individuellement sous la même balise  <form>  :

# blog/templates/blog/create_blog_post.html
{% extends 'base.html' %}
{% block content %}
    <h2>Écrire un billet</h2>
    <form method="post" enctype="multipart/form-data">
        {{ blog_form.as_p }}
        {{ photo_form.as_p }}
        {% csrf_token %}
        <button type="submit" >Publier</button>
    </form>
{% endblock content %}

Maintenant que les deux formulaires sont sous la même balise  <form>  , un clic sur le bouton « envoyer » enverra les données  POST  des deux formulaires en même temps. Voyons comment gérer cela dans la vue.

Étape 3 : Écrivez la vue

Avant d’apprendre à gérer l’aspect  POST  de la requête, définissons la vue pour inclure à la fois le  photo_form  et le  blog_form  dans le  context  et retourner le rendu du gabarit dans la réponse HTTP.

# blog/views.py
@login_required
    def blog_and_photo_upload(request):
    blog_form = forms.BlogForm()
    photo_form = forms.PhotoForm()
    if request.method == 'POST':
        # handle the POST request here
    context = {
        'blog_form': blog_form,
        'photo_form': photo_form,
}
return render(request, 'blog/create_blog_post.html', context=context)

Désormais, vous pouvez fournir les données  POST  aux formulaires, puis utiliser la méthode  save()  pour instancier les modèles.

# blog/forms.py
@login_required
def blog_and_photo_upload(request):
    blog_form = forms.BlogForm()
    photo_form = forms.PhotoForm()
    if request.method == 'POST':
        blog_form = forms.BlogForm(request.POST)
        photo_form = forms.PhotoForm(request.POST, request.FILES)
        if all([blog_form.is_valid(), photo_form.is_valid()]):
            blog_form.save()
            photo_form.save()
    context = {
        'blog_form': blog_form,
        'photo_form': photo_form,
}
return render(request, 'blog/create_blog_post.html', context=context)

La vue va maintenant recevoir la requête  POST  et créer une instance des modèles  Blog  et  Photo  , si les deux formulaires sont valides.

Pourquoi ne pas simplement utiliser  if  blog_form.is_valid() and photo_form.is_valid()   ?

L’exécution de la méthode  is_valid()  sur un formulaire ne se contente pas de vérifier sa validité. Elle génère aussi des messages d’erreur pour tous les champs qui auraient des entrées non valides. Ces messages sont affichés en tant que feedback sur le front-end.

Lorsque vous exécutez une instruction  if  condition_a and condition_b  , Python exige que les deux conditions soient « Truthy » (évaluées à True). Si l’instruction conclut que   condition_a  est « Falsy » (évaluée à False), elle cessera immédiatement d’exécuter la ligne et sautera directement au prochain bloc de code.

Cela signifie que si vous avez l’instruction   if blog_form.is_valid() and photo_form.is_valid()  , et que  blog_form.is_valid()  renvoie  False  , alors  photo_form.is_valid()  ne sera jamais exécuté, et ne générera pas de message d’erreur.

L’approche actuelle marche très bien si vous voulez uniquement instancier ces modèles. Néanmoins, nous devrons lier les deux objets à l’utilisateur qui les a créés, et remplir le champ  photo   pour l’objet  Blog  . Ajustons la vue pour prendre ces éléments en compte.

@login_required
def blog_and_photo_upload(request):
    blog_form = forms.BlogForm()
    photo_form = forms.PhotoForm()
    if request.method == 'POST':
        blog_form = forms.BlogForm(request.POST)
        photo_form = forms.PhotoForm(request.POST, request.FILES)
        if all([blog_form.is_valid(), photo_form.is_valid()]):
            photo = photo_form.save(commit=False)
            photo.uploader = request.user
            photo.save()
            blog = blog_form.save(commit=False)
            blog.author = request.user
            blog.photo = photo
            blog.save()
            return redirect('home')
    context = {
        'blog_form': blog_form,
        'photo_form': photo_form,
}
return render(request, 'blog/create_blog_post.html', context=context)

La vue met désormais correctement à jour tous les champs avant de les enregistrer effectivement dans la base de données. La  photo  et le  blog  seront liés l’un à l’autre, ainsi qu’à l’utilisateur qui les a créés.

Étape 4 : Ajoutez le modèle d’URL

Ajoutons maintenant le nouveau modèle d’URL :

# fotoblog/urls.py
urlpatterns = [
    …
    path('blog/create', blog.views.blog_and_photo_upload, name='blog_create'),
]

Étape 5 : Ajoutez «Écrire un billet» à la barre latérale

Pour finir, ajoutez le lien « Écrire un billet » au gabarit de base.

# templates/base.html
...
<p><a href="{% url 'home' %}">Accueil</a></p>
<p><a href="{% url 'blog_create' %}">Écrire un billet</a></p>
<p><a href="{% url 'photo_upload' %}">Télécharger une photo</a></p>
...

Puis, naviguez sur la nouvelle page et voyez de quoi elle a l’air.

Super ! Vous avez maintenant un moyen de créer des publications blog et photo simultanément. En revanche, il n’y a pas de moyen de les visualiser une fois qu’elles sont créées. Ajoutons maintenant cette possibilité.

Ajoutez une page de visualisation d’un billet de blog

Nous allons inclure des posts de blog sur la page d’accueil, mais nous ne voulons tout de même pas voir tout le texte de chaque billet du blog. Ajoutons donc une autre page, où l’utilisateur peut voir un billet complet.

Étape 1 : Ajoutez la vue pour la page de visualisation d’un billet

Maintenant, ajoutons la vue pour voir des billets individuels.

# blog/views.py

from django.shortcuts import get_object_or_404
@login_required
def view_blog(request, blog_id):
    blog = get_object_or_404(models.Blog, id=blog_id)
    return render(request, 'blog/view_blog.html', {'blog': blog})

Étape 2 : Ajoutez le gabarit pour la page de visualisation d’un billet

Et maintenant, ajoutez le gabarit pour la page de visualisation d’un billet.

# blog/templates/blog/view_blog.html
{% extends 'base.html' %}
{% block content %}
    <h2>{{ blog.title }}</h2>
    <img src="{{ blog.photo.image.url }}">
    <p>{{ blog.photo.caption }}</p>
    <p>{{ blog.content }}</p>
{% endblock content %}

Étape 3 : Ajoutez le modèle d’URL pour la page de visualisation d’un billet

Et maintenant, ajoutez le modèle d’URL pour la page de visualisation d’un billet.

# fotoblog/urls.py
urlpatterns = [
    …
    path('blog/<int:blog_id>', blog.views.view_blog, name='view_blog'),
]

Étape 4 : Récupérez les instances de Blogdans la vue home  

Maintenant que la page de visualisation d’un billet est construite, récupérons les instances de  Blog  pour la page d’accueil.

# blog/views.py
@login_required
def home(request):
    photos = models.Photo.objects.all()
    blogs = models.Blog.objects.all()
    return render(request, 'blog/home.html', context={'photos': photos, 'blogs': blogs})

Étape 5 : Mettez à jour le gabarit home.html  

Et ensuite, affichez-les dans le gabarit, avec un lien vers la page du billet de blog.

# blog/templates/blog/home.html
{% extends 'base.html' %}
{% block content %}
    ...
    <h2>Blog</h2>
    <div class="grid-container">
        {% for blog in blogs %}
            <div class="post">
                <a href="{% url 'view_blog' blog.id %}">
                    <h4>Billet : {{ blog.title }}</h4>
                    <img src="{{ blog.photo.image.url }}">
                </a>
            </div>
        {% endfor %}
    </div>
{% endblock content %}

Étape 6 : Testez le service vous-même

Bravo ! Vous avez ajouté beaucoup de fonctionnalités ! Prenez le temps de créer quelques billets sur le blog, visualisez-les depuis la page d’accueil, puis cliquez dedans pour voir leur contenu.

Une fois que vos changements vous conviendront, vous serez prêt à passer à l'étape suivante.

En résumé 

  • Vous pouvez inclure plusieurs formulaires dans une vue, pour instancier plusieurs modèles simultanément.

  • Si vous faites le rendu de ces formulaires au sein des mêmes balises   <form>  dans le gabarit, ils seront soumis dans la même requête  POST  .

  • Vous pouvez remplir ces deux formulaires avec l’objet   request.POST  dans la vue, et instancier plusieurs modèles depuis la même requête.

Maintenant que vous pouvez instancier plusieurs modèles en envoyant un seul formulaire, vous pouvez vous plonger dans le prochain chapitre. Vous allez y apprendre à gérer plusieurs formulaires sur une seule page.

Exemple de certificat de réussite
Exemple de certificat de réussite