Comment utiliser les relations de base de données un à plusieurs avec Flask-SQLAlchemy

De Get Docs
Aller à :navigation, rechercher

L'auteur a sélectionné le Free and Open Source Fund pour recevoir un don dans le cadre du programme Write for DOnations.

Introduction

Flask est un framework Web Python léger qui fournit des outils et des fonctionnalités utiles pour créer des applications Web dans le langage Python. SQLAlchemy est une boîte à outils SQL qui fournit un accès efficace et performant aux bases de données relationnelles. Il fournit des moyens d'interagir avec plusieurs moteurs de base de données tels que SQLite, MySQL et PostgreSQL. Il vous donne accès aux fonctionnalités SQL de la base de données. Et il vous donne également un mappeur relationnel d'objet (ORM), qui vous permet de faire des requêtes et de gérer des données à l'aide d'objets et de méthodes Python simples. Flask-SQLAlchemy est une extension Flask qui facilite l'utilisation de SQLAlchemy avec Flask, vous fournissant des outils et des méthodes pour interagir avec votre base de données dans vos applications Flask via SQLAlchemy.

Une relation de base de données un-à-plusieurs est une relation entre deux tables de base de données où un enregistrement dans une table peut référencer plusieurs enregistrements dans une autre table. Par exemple, dans une application de blog, une table pour stocker des publications peut avoir une relation un-à-plusieurs avec une table pour stocker des commentaires. Chaque article peut faire référence à de nombreux commentaires, et chaque commentaire fait référence à un seul article ; par conséquent, un message a une relation avec de nombreux commentaires. La table de publication est une table parent, tandis que la table de commentaires est une table enfant — un enregistrement dans la table parent peut référencer plusieurs enregistrements dans la table enfant. Cette relation est importante pour permettre l'accès aux données associées dans chaque table.

Dans ce didacticiel, vous allez créer un petit système de blog qui montre comment créer des relations un-à-plusieurs à l'aide de l'extension Flask-SQLAlchemy. Vous allez créer une relation entre les articles et les commentaires, où chaque article de blog peut avoir plusieurs commentaires.

Conditions préalables

Étape 1 - Installation de Flask et Flask-SQLAlchemy

Dans cette étape, vous allez installer les packages nécessaires pour votre application.

Avec votre environnement virtuel activé, utilisez pip pour installer Flask et Flask-SQLAlchemy :

pip install Flask Flask-SQLAlchemy

Une fois l'installation terminée avec succès, vous verrez une ligne semblable à la suivante à la fin de la sortie :

OutputSuccessfully installed Flask-2.1.1 Flask-SQLAlchemy-2.5.1 Jinja2-3.1.1 MarkupSafe-2.1.1 SQLAlchemy-1.4.35 Werkzeug-2.1.1 click-8.1.2 greenlet-1.1.2 itsdangerous-2.1.2

Une fois les packages Python requis installés, vous configurerez ensuite la base de données.

Étape 2 - Configuration de la base de données et des modèles

Dans cette étape, vous allez configurer votre base de données et créer des modèles de base de données SQLAlchemy ' — des classes Python qui représentent vos tables de base de données. Vous allez créer un modèle pour vos articles de blog et un modèle pour les commentaires. Vous allez lancer la base de données, créer un tableau pour les publications et ajouter un tableau pour les commentaires en fonction des modèles que vous déclarerez. Vous insérerez également quelques messages et commentaires dans votre base de données.

Configuration de la connexion à la base de données

Ouvrez un fichier nommé app.py dans votre répertoire flask_app. Ce fichier contiendra du code pour configurer la base de données et vos itinéraires Flask :

nano app.py

Ce fichier se connectera à une base de données SQLite appelée database.db et aura deux classes : une classe appelée Post qui représente la table des messages de votre base de données et une classe Comment représentant les commentaires table. Ce fichier contiendra également vos routes Flask. Ajoutez les instructions import suivantes en haut de app.py :

flacon_app/app.py

import os
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy

Ici, vous importez le module os , qui vous donne accès à diverses interfaces du système d'exploitation. Vous l'utiliserez pour construire un chemin de fichier pour votre fichier de base de données database.db.

Depuis le package flask, vous importez ensuite les helpers nécessaires dont vous avez besoin pour votre application : la classe Flask pour créer une instance d'application Flask, la fonction render_template() pour rendre les modèles, la l'objet request pour gérer les requêtes, la fonction url_for() pour construire des URL pour les routes et la fonction redirect() pour rediriger les utilisateurs. Pour plus d'informations sur les itinéraires et les modèles, voir Comment utiliser des modèles dans une application Flask.

Vous importez ensuite la classe SQLAlchemy de l'extension Flask-SQLAlchemy, qui vous donne accès à toutes les fonctions et classes de SQLAlchemy, en plus des aides et des fonctionnalités qui intègrent Flask à SQLAlchemy. Vous l'utiliserez pour créer un objet de base de données qui se connecte à votre application Flask, vous permettant de créer et de manipuler des tables à l'aide de classes, d'objets et de fonctions Python sans avoir besoin d'utiliser le langage SQL.

Sous les importations, vous allez configurer un chemin de fichier de base de données, instancier votre application Flask, configurer et connecter votre application à SQLAlchemy. Ajoutez le code suivant :

flacon_app/app.py

basedir = os.path.abspath(os.path.dirname(__file__))

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] =\
           'sqlite:///' + os.path.join(basedir, 'database.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False


db = SQLAlchemy(app)

Ici, vous construisez un chemin pour votre fichier de base de données SQLite. Vous définissez d'abord un répertoire de base comme répertoire courant. Vous utilisez la fonction os.path.abspath() pour obtenir le chemin absolu du répertoire du fichier courant. La variable spéciale __file__ contient le chemin du fichier app.py courant. Vous stockez le chemin absolu du répertoire de base dans une variable appelée basedir.

Vous créez ensuite une instance d'application Flask appelée app, que vous utilisez pour configurer deux clés de configuration Flask-SQLAlchemy [1] :

  • SQLALCHEMY_DATABASE_URI : L'URI de la base de données pour spécifier la base de données avec laquelle vous souhaitez établir une connexion. Dans ce cas, l'URI suit le format sqlite:///path/to/database.db. Vous utilisez la fonction os.path.join() pour joindre intelligemment le répertoire de base que vous avez construit et stocké dans la variable basedir et le nom de fichier database.db. Cela se connectera à un fichier de base de données database.db dans votre répertoire flask_app. Le fichier sera créé une fois que vous lancerez la base de données.
  • SQLALCHEMY_TRACK_MODIFICATIONS : Une configuration pour activer ou désactiver le suivi des modifications d'objets. Vous le réglez sur False pour désactiver le suivi et utiliser moins de mémoire. Pour plus d'informations, consultez la page de configuration dans la documentation de Flask-SQLAlchemy.

Remarque : Si vous souhaitez utiliser un autre moteur de base de données tel que PostgreSQL ou MySQL, vous devez utiliser l'URI approprié.

Pour PostgreSQL, utilisez le format suivant :

postgresql://username:[email protected]:port/database_name

Pour MySQL :

mysql://username:[email protected]:port/database_name

Pour plus d'informations, consultez la documentation SQLAlchemy pour la configuration du moteur.


Après avoir configuré SQLAlchemy en définissant un URI de base de données et en désactivant le suivi, vous créez un objet de base de données à l'aide de la classe SQLAlchemy, en transmettant l'instance d'application pour connecter votre application Flask à SQLAlchemy. Vous stockez votre objet de base de données dans une variable appelée db. Vous utiliserez cet objet db pour interagir avec votre base de données.

Déclarer les tables

Une fois la connexion à la base de données établie et l'objet de base de données créé, vous utiliserez l'objet de base de données pour créer une table de base de données pour les publications et une pour les commentaires. Les tables sont représentées par un model — une classe Python qui hérite d'une classe de base que Flask-SQLAlchemy fournit via l'instance de base de données db que vous avez créée précédemment. Pour définir les tableaux d'articles et de commentaires comme modèles, ajoutez les deux classes suivantes à votre fichier app.py :

flacon_app/app.py

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100))
    content = db.Column(db.Text)
    comments = db.relationship('Comment', backref='post')

    def __repr__(self):
        return f'<Post "{self.title}">'


class Comment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text)
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'))

    def __repr__(self):
        return f'<Comment "{self.content[:20]}...">'

Ici, vous créez un modèle Post et un modèle Comment, qui héritent de la classe db.Model.

Le modèle Post représente la table des messages. Vous utilisez la classe db.Column pour définir ses colonnes. Le premier argument représente le type de colonne et les arguments supplémentaires représentent la configuration de la colonne.

Vous définissez les colonnes suivantes pour le modèle Post :

  • id : ID de la publication. Vous le définissez comme un entier avec db.Integer. primary_key=True définit cette colonne comme une clé primaire, qui lui attribuera une valeur unique par la base de données pour chaque entrée (c'est-à-dire chaque publication).
  • title : le titre de la publication. Une chaîne d'une longueur maximale de 100 caractères.
  • content : le contenu de la publication. db.Text indique que la colonne contient des textes longs.

L'attribut de classe comments définit une relation un-à-plusieurs entre le modèle Post et le modèle Comment. Vous utilisez la méthode db.relationship() en lui passant le nom du modèle de commentaires (Comment dans ce cas). Vous utilisez le paramètre backref pour ajouter une référence arrière qui se comporte comme une colonne au modèle Comment. De cette façon, vous pouvez accéder à la publication sur laquelle le commentaire a été publié à l'aide d'un attribut post. Par exemple, si vous avez un objet de commentaire dans une variable appelée comment, vous pourrez accéder à la publication à laquelle appartient le commentaire en utilisant comment.post. Vous verrez un exemple le démontrant plus tard.

Voir la documentation SQLAlchemy pour les types de colonne autres que les types que vous avez utilisés dans le bloc de code précédent.

La fonction spéciale __repr__ vous permet de donner à chaque objet une représentation sous forme de chaîne pour le reconnaître à des fins de débogage.

Le modèle Comment représente la table de commentaires. Vous lui définissez les colonnes suivantes :

  • id : ID de commentaire. Vous le définissez comme un entier avec db.Integer. primary_key=True définit cette colonne comme une clé primaire, qui lui attribuera une valeur unique par la base de données pour chaque entrée (c'est-à-dire chaque commentaire).
  • content : Le contenu du commentaire. db.Text indique que la colonne contient des textes longs.
  • post_id : un entier clé étrangère que vous construisez à l'aide de la classe db.ForeignKey(), qui est une clé qui relie une table à une autre, en utilisant la clé primaire de cette table. Cela lie un commentaire à un article en utilisant la clé primaire de l'article, qui est son ID. Ici, la table post est une table parent, qui indique que chaque article comporte de nombreux commentaires. La table comment est une table enfant '. Chaque commentaire est lié à un article parent à l'aide de l'ID de l'article. Par conséquent, chaque commentaire a une colonne post_id qui peut être utilisée pour accéder à la publication sur laquelle le commentaire a été publié.

La fonction spéciale __repr__ du modèle Comment affiche les 20 premiers caractères du contenu du commentaire pour donner à un objet de commentaire une représentation sous forme de chaîne courte.

Le fichier app.py aura maintenant l'aspect suivant :

flacon_app/app.py

import os
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy

basedir = os.path.abspath(os.path.dirname(__file__))

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] =\
           'sqlite:///' + os.path.join(basedir, 'database.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False


db = SQLAlchemy(app)


class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100))
    content = db.Column(db.Text)
    comments = db.relationship('Comment', backref='post')

    def __repr__(self):
        return f'<Post "{self.title}">'


class Comment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text)
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'))

    def __repr__(self):
        return f'<Comment "{self.content[:20]}...">'

Enregistrez et fermez app.py.

Création de la base de données

Maintenant que vous avez défini la connexion à la base de données et les modèles de publication et de commentaire, vous allez utiliser le shell Flask pour créer votre base de données et vos tables de publication et de commentaire en fonction des modèles que vous avez déclarés.

Avec votre environnement virtuel activé, définissez le fichier app.py comme votre application Flask à l'aide de la variable d'environnement FLASK_APP :

export FLASK_APP=app

Ouvrez ensuite le shell Flask à l'aide de la commande suivante dans votre répertoire flask_app :

flask shell

Un shell interactif Python sera ouvert. Ce shell spécial exécute des commandes dans le contexte de votre application Flask, afin que les fonctions Flask-SQLAlchemy que vous appellerez soient connectées à votre application.

Importez l'objet de base de données et les modèles de publication et de commentaire, puis exécutez la fonction db.create_all() pour créer les tables associées à vos modèles :

from app import db, Post, Comment
db.create_all()

Laissez le shell en cours d'exécution, ouvrez une autre fenêtre de terminal et accédez à votre répertoire flask_app. Vous verrez maintenant un nouveau fichier appelé database.db dans flask_app.

Remarque : La fonction db.create_all() ne recrée ni ne met à jour une table si elle existe déjà. Par exemple, si vous modifiez votre modèle en ajoutant une nouvelle colonne et exécutez la fonction db.create_all(), la modification que vous apportez au modèle ne sera pas appliquée à la table si la table existe déjà dans la base de données. La solution consiste à supprimer toutes les tables de base de données existantes avec la fonction db.drop_all(), puis à les recréer avec la fonction db.create_all() comme suit :

db.drop_all()
db.create_all()

Cela appliquera les modifications que vous apportez à vos modèles, mais supprimera également toutes les données existantes dans la base de données. Pour mettre à jour la structure de la base de données et conserver les données existantes, vous devrez utiliser la schema migration, qui vous permet de modifier vos tables et de conserver les données. Vous pouvez utiliser l'extension Flask-Migrate pour effectuer des migrations de schéma SQLAlchemy via l'interface de ligne de commande Flask.


Si vous recevez une erreur, assurez-vous que l'URI de votre base de données et la déclaration de votre modèle sont corrects.

Remplir les tables

Après avoir créé la base de données et les tables de publications et de commentaires, vous allez créer un fichier dans votre répertoire flask_app pour ajouter des publications et des commentaires à votre base de données.

Ouvrez un nouveau fichier nommé init_db.py :

nano init_db.py

Ajoutez-y le code suivant. Ce fichier créera trois objets de publication et quatre objets de commentaire, et les ajoutera à la base de données :

flask_app/init_db.py

from app import db, Post, Comment

post1 = Post(title='Post The First', content='Content for the first post')
post2 = Post(title='Post The Second', content='Content for the Second post')
post3 = Post(title='Post The Third', content='Content for the third post')

comment1 = Comment(content='Comment for the first post', post=post1)
comment2 = Comment(content='Comment for the second post', post=post2)
comment3 = Comment(content='Another comment for the second post', post_id=2)
comment4 = Comment(content='Another comment for the first post', post_id=1)


db.session.add_all([post1, post2, post3])
db.session.add_all([comment1, comment2, comment3, comment4])

db.session.commit()

Enregistrez et fermez le fichier.

Ici, vous importez l'objet de base de données, le modèle Post et le modèle Comment à partir du fichier app.py.

Vous créez quelques objets de publication à l'aide du modèle Post, en transmettant le titre de la publication au paramètre title et le contenu de la publication au paramètre content.

Vous créez ensuite quelques objets de commentaire, en transmettant le contenu du commentaire. Vous disposez de deux méthodes pour associer un commentaire à la publication à laquelle il appartient. Vous pouvez passer l'objet post au paramètre post comme illustré dans les objets comment1 et comment2. Et vous pouvez également transmettre l'ID de publication au paramètre post_id, comme illustré dans les objets comment3 et comment4. Vous pouvez donc simplement passer l'ID entier du message si vous n'avez pas l'objet post dans votre code.

Après avoir défini les objets de publication et de commentaire, vous utilisez db.session.add_all() pour ajouter tous les objets de publication et de commentaire à la session de base de données, qui gère les transactions. Ensuite, vous utilisez la méthode db.session.commit() pour valider la transaction et appliquer les modifications à la base de données. Pour plus d'informations sur les sessions de base de données SQLAlchemy, consultez l'étape 2 du didacticiel Comment utiliser Flask-SQLAlchemy pour interagir avec les bases de données dans une application Flask.

Exécutez le fichier init_db.py pour exécuter le code et ajouter les données à la base de données :

python init_db.py

Pour jeter un œil aux données que vous avez ajoutées à votre base de données, ouvrez le flacon pour interroger tous les articles et afficher leurs titres et le contenu des commentaires de chaque article :

flask shell

Exécutez le code suivant. Cela interroge tous les messages et affiche chaque titre de message et les commentaires de chaque message en dessous :

from app import Post

posts = Post.query.all()

for post in posts:
    print(f'## {post.title}')
    for comment in post.comments:
            print(f'> {comment.content}')
    print('----')

Ici, vous importez le modèle Post à partir du fichier app.py. Vous interrogez toutes les publications qui existent dans la base de données à l'aide de la méthode all() sur l'attribut query et enregistrez le résultat dans une variable appelée posts. Ensuite, vous utilisez une boucle for pour parcourir chaque élément de la variable posts. Vous imprimez le titre, puis utilisez une autre boucle for pour parcourir chaque commentaire appartenant à la publication. Vous accédez aux commentaires de la publication en utilisant post.comments. Vous imprimez le contenu du commentaire, puis imprimez la chaîne '----' pour séparer les publications.

Vous obtiendrez la sortie suivante :

Output
## Post The First
> Comment for the first post
> Another comment for the first post
----
## Post The Second
> Comment for the second post
> Another comment for the second post
----
## Post The Third
----

Comme vous pouvez le voir, vous pouvez accéder aux données de chaque publication et aux commentaires de chaque publication avec très peu de code.

Quittez maintenant le shell :

exit()

À ce stade, vous avez plusieurs messages et commentaires dans votre base de données. Ensuite, vous allez créer un itinéraire Flask pour la page d'index et y afficher tous les articles de votre base de données.

Étape 3 - Affichage de tous les messages

Dans cette étape, vous allez créer un itinéraire et un modèle pour afficher tous les articles de la base de données sur la page d'index.

Ouvrez votre fichier app.py pour y ajouter un itinéraire pour la page d'index :

nano app.py

Ajoutez la route suivante à la fin du fichier :

flacon_app/app.py

# ...

@app.route('/')
def index():
    posts = Post.query.all()
    return render_template('index.html', posts=posts)

Enregistrez et fermez le fichier.

Ici, vous créez une fonction de vue index() à l'aide du décorateur app.route(). Dans cette fonction, vous interrogez la base de données et obtenez tous les messages comme vous l'avez fait à l'étape précédente. Vous stockez le résultat de la requête dans une variable appelée posts, puis vous le transmettez à un fichier de modèle index.html que vous rendez à l'aide de la fonction d'assistance render_template().

Avant de créer le fichier de modèle index.html sur lequel vous afficherez les articles existants dans la base de données, vous allez d'abord créer un modèle de base, qui contiendra tout le code HTML de base que d'autres modèles utiliseront également pour éviter le code répétition. Ensuite, vous allez créer le fichier de modèle index.html que vous avez rendu dans votre fonction index(). Pour en savoir plus sur les modèles, consultez Comment utiliser des modèles dans une application Flask.

Créez un répertoire templates, puis ouvrez un nouveau modèle nommé base.html :

mkdir templates
nano templates/base.html

Ajoutez le code suivant dans le fichier base.html :

flask_app/templates/base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %} - FlaskApp</title>
    <style>
        .title {
            margin: 5px;
        }

        .content {
            margin: 5px;
            width: 100%;
            display: flex;
            flex-direction: row;
            flex-wrap: wrap;
        }

        .comment {
            padding: 10px;
            margin: 10px;
            background-color: #fff;
        }

        .post {
            flex: 20%;
            padding: 10px;
            margin: 5px;
            background-color: #f3f3f3;
            inline-size: 100%;
        }

        .title a {
            color: #00a36f;
            text-decoration: none;
        }

        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }

    </style>
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">FlaskApp</a>
        <a href="#">Comments</a>
        <a href="#">About</a>
    </nav>
    <hr>
    <div class="content">
        {% block content %} {% endblock %}
    </div>
</body>
</html>

Enregistrez et fermez le fichier.

Ce modèle de base contient tout le code HTML standard que vous devrez réutiliser dans vos autres modèles. Le bloc title sera remplacé pour définir un titre pour chaque page, et le bloc content sera remplacé par le contenu de chaque page. La barre de navigation comporte trois liens : un pour la page d'index, qui renvoie à la fonction d'affichage index() à l'aide de la fonction d'assistance url_for(), un pour une page Commentaires, et un pour une page À propos de si vous choisissez d'en ajouter une à votre application. Vous modifierez ce fichier plus tard après avoir ajouté une page pour afficher tous les derniers commentaires afin de rendre le lien Commentaires fonctionnel.

Ensuite, ouvrez un nouveau fichier modèle index.html. Il s'agit du modèle que vous avez référencé dans le fichier app.py :

nano templates/index.html

Ajoutez-y le code suivant :

flask_app/templates/index.html

{% extends 'base.html' %}

{% block content %}
    <span class="title"><h1>{% block title %} Posts {% endblock %}</h1></span>
    <div class="content">
        {% for post in posts %}
            <div class="post">
                <p><b>#{{ post.id }}</b></p>
                <b>
                    <p class="title">
                        <a href="#">
                            {{ post.title }}
                        </a>
                    </p>
                </b>
                <div class="content">
                    <p>{{ post.content }}</p>
                </div>
                <hr>
            </div>
        {% endfor %}
    </div>
{% endblock %}

Enregistrez et fermez le fichier.

Ici, vous étendez le modèle de base et remplacez le contenu du bloc de contenu. Vous utilisez un titre <h1> qui sert également de titre. Vous utilisez un Jinja for loop dans la ligne {% for post in posts %} pour parcourir chaque message dans la variable posts que vous avez transmise de la fonction d'affichage index() à cette modèle. Vous affichez l'ID de la publication, son titre et le contenu de la publication. Le titre du message sera ensuite lié à une page qui affiche le message individuel et ses commentaires.

Dans votre répertoire flask_app avec votre environnement virtuel activé, informez Flask de l'application (app.py dans ce cas) à l'aide de la variable d'environnement FLASK_APP. Définissez ensuite la variable d'environnement FLASK_ENV sur development pour exécuter l'application en mode développement et accéder au débogueur. Pour plus d'informations sur le débogueur Flask, consultez Comment gérer les erreurs dans une application Flask. Utilisez les commandes suivantes pour ce faire :

export FLASK_APP=app
export FLASK_ENV=development

Ensuite, lancez l'application :

flask run

Avec le serveur de développement en cours d'exécution, accédez à l'URL suivante à l'aide de votre navigateur :

http://127.0.0.1:5000/

Vous verrez les publications que vous avez ajoutées à la base de données dans une page semblable à la suivante :

Vous avez affiché les messages que vous avez dans votre base de données sur la page d'index. Ensuite, vous allez créer un itinéraire pour une page de publication, où vous afficherez les détails de chaque publication et ses commentaires en dessous.

Étape 4 - Affichage d'un seul article et de ses commentaires

Dans cette étape, vous allez créer un itinéraire et un modèle pour afficher les détails de chaque article sur une page dédiée, ainsi que les commentaires de l'article en dessous.

À la fin de cette étape, l'URL http://127.0.0.1:5000/1 sera une page qui affiche le premier message (car il a l'ID 1) et ses commentaires. L'URL http://127.0.0.1:5000/ID affichera la publication avec le numéro ID associé, s'il existe.

Laissez le serveur de développement en cours d'exécution et ouvrez une nouvelle fenêtre de terminal.

Ouvrez app.py pour modification :

nano app.py

Ajoutez la route suivante à la fin du fichier :

flacon_app/app.py

# ...

@app.route('/<int:post_id>/')
def post(post_id):
    post = Post.query.get_or_404(post_id)
    return render_template('post.html', post=post)

Enregistrez et fermez le fichier.

Ici, vous utilisez la route '/<int:post_id>/', avec int: étant un convertisseur qui convertit la chaîne par défaut dans l'URL en un entier. post_id est la variable URL qui déterminera la publication que vous afficherez sur la page.

L'ID est transmis de l'URL à la fonction d'affichage post() via le paramètre post_id. Dans la fonction, vous interrogez la table des publications et récupérez une publication par son ID à l'aide de la méthode get_or_404(). Cela enregistrera les données de publication dans la variable post si elle existe, et répondra avec une erreur HTTP 404 Not Found si aucune publication avec l'ID donné n'existe dans la base de données.

Vous affichez un modèle appelé post.html et lui transmettez le message que vous avez récupéré.

Ouvrez ce nouveau fichier modèle post.html :

nano templates/post.html

Tapez-y le code suivant. Ce modèle sera similaire au modèle index.html, sauf qu'il n'affichera qu'un seul article :

flask_app/templates/post.html

{% extends 'base.html' %}

{% block content %}
    <span class="title"><h1>{% block title %} {{ post.title }}  {% endblock %}</h1></span>
    <div class="content">
            <div class="post">
                <p><b>#{{ post.id }}</b></p>
                <b>
                    <p class="title">{{ post.title }}</p>
                </b>
                <div class="content">
                    <p>{{ post.content }}</p>
                </div>
                <hr>
                <h3>Comments</h3>
                {% for comment in post.comments %}
                    <div class="comment">
                        <p>#{{ comment.id }}</p>
                        <p>{{ comment.content }}</p>
                    </div>
                {% endfor %}
            </div>
    </div>
{% endblock %}

Enregistrez et fermez le fichier.

Ici, vous étendez le modèle de base, définissez le titre de la publication comme titre de page, affichez l'ID de la publication, le titre de la publication et le contenu de la publication. Ensuite, vous parcourez les commentaires de publication disponibles via post.comments. Vous affichez l'ID de commentaire et le contenu du commentaire.

Utilisez votre navigateur pour accéder à l'URL du deuxième message :

http://127.0.0.1:5000/2/

Vous verrez une page semblable à la suivante :

Ensuite, modifiez index.html pour que le titre du message soit lié au message individuel :

nano templates/index.html

Modifiez la valeur de l'attribut href du lien du titre de l'article à l'intérieur de la boucle for :

flask_app/templates/index.html

...

{% for post in posts %}
    <div class="post">
        <p><b>#{{ post.id }}</b></p>
        <b>
            <p class="title">
                <a href="{{ url_for('post', post_id=post.id)}}">
                {{ post.title }}
                </a>
            </p>
        </b>
        <div class="content">
            <p>{{ post.content }}</p>
        </div>
        <hr>
    </div>
{% endfor %}

Enregistrez et fermez le fichier.

Accédez à votre page d'index ou actualisez-la :

http://127.0.0.1:5000/

Cliquez sur chacun des titres de publication sur la page d'index. Vous verrez maintenant que chaque publication est liée à la page de publication appropriée.

Vous avez maintenant créé une page pour afficher les messages individuels. Ensuite, vous allez ajouter un formulaire Web à la page de publication pour permettre aux utilisateurs d'ajouter de nouveaux commentaires.

Étape 5 - Ajouter de nouveaux commentaires

Dans cette étape, vous allez modifier l'itinéraire /<int:post_id>/ et sa fonction d'affichage post(), qui gère l'affichage d'un message individuel. Vous ajouterez un formulaire Web sous chaque article pour permettre aux utilisateurs d'ajouter des commentaires à cet article, puis vous gérerez la soumission du commentaire et l'ajouterez à la base de données.

Commencez par ouvrir le fichier de modèle post.html pour ajouter un formulaire Web composé d'une zone de texte pour le contenu du commentaire et d'un bouton d'envoi Ajouter un commentaire.

nano templates/post.html

Modifiez le fichier en ajoutant un formulaire sous l'en-tête Comments H3 et directement au-dessus de la boucle for :

flask_app/templates/post.html

<hr>
<h3>Comments</h3>
<form method="post">
    <p>
        <textarea name="content"
                    placeholder="Comment"
                    cols="60"
                    rows="5"></textarea>
    </p>

    <p>
        <button type="submit">Add comment</button>
    </p>
</form>
{% for comment in post.comments %}

Enregistrez et fermez le fichier.

Ici, vous ajoutez une balise <form> avec l'attribut method défini sur post pour indiquer que le formulaire soumettra une requête POST.

Vous disposez d'une zone de texte pour le contenu du commentaire et d'un bouton d'envoi.

Avec le serveur de développement en cours d'exécution, utilisez votre navigateur pour accéder à une publication :

http://127.0.0.1:5000/2/

Vous verrez une page semblable à la suivante :

Ce formulaire envoie une requête POST à la fonction d'affichage post(), mais comme il n'y a pas de code pour gérer la soumission du formulaire, le formulaire ne fonctionne pas actuellement.

Ensuite, vous ajouterez du code à la fonction d'affichage post() pour gérer la soumission du formulaire et ajouter le nouveau commentaire à la base de données. Ouvrez app.py pour gérer la requête POST soumise par l'utilisateur :

nano app.py

Modifiez l'itinéraire /<int:post_id>/ et sa fonction d'affichage post() pour qu'ils se présentent comme suit :

flacon_app/app.py

@app.route('/<int:post_id>/', methods=('GET', 'POST'))
def post(post_id):
    post = Post.query.get_or_404(post_id)
    if request.method == 'POST':
        comment = Comment(content=request.form['content'], post=post)
        db.session.add(comment)
        db.session.commit()
        return redirect(url_for('post', post_id=post.id))

    return render_template('post.html', post=post)

Enregistrez et fermez le fichier.

Vous autorisez les requêtes GET et POST à l'aide du paramètre methods. Les requêtes GET sont utilisées pour récupérer les données du serveur. Les requêtes POST sont utilisées pour publier des données sur une route spécifique. Par défaut, seules les requêtes GET sont autorisées.

Dans la condition if request.method == 'POST', vous gérez la requête POST que l'utilisateur soumettra via le formulaire. Vous créez un objet de commentaire à l'aide du modèle Comment, en lui transmettant le contenu du commentaire soumis que vous extrayez de l'objet request.form. Vous spécifiez la publication à laquelle appartient le commentaire à l'aide du paramètre post, en lui transmettant l'objet post que vous avez récupéré à l'aide de l'ID de publication, avec la méthode get_or_404().

Vous ajoutez l'objet de commentaire que vous avez construit à la session de base de données, validez la transaction et redirigez vers la page de publication.

Maintenant, actualisez la page de publication sur votre navigateur, écrivez un commentaire et soumettez-le. Vous verrez votre nouveau commentaire sous la publication.

Vous disposez maintenant d'un formulaire Web qui permet aux utilisateurs d'ajouter des commentaires à une publication. Pour en savoir plus sur les formulaires Web, consultez Comment utiliser les formulaires Web dans une application Flask. Pour une méthode plus avancée et plus sécurisée de gestion des formulaires Web, voir Comment utiliser et valider les formulaires Web avec Flask-WTF. Ensuite, vous allez ajouter une page qui affiche tous les commentaires de la base de données et les messages sur lesquels ils ont été publiés.

Étape 6 - Affichage de tous les commentaires

Dans cette étape, vous allez ajouter une page Commentaires où vous afficherez tous les commentaires de la base de données, en les classant en affichant les commentaires les plus récents en premier. Chaque commentaire aura le titre et le lien de la publication sur laquelle le commentaire a été publié.

Ouvrez app.py :

nano app.py

Ajoutez la route suivante à la fin du fichier. Cela récupère tous les commentaires dans la base de données, classés par le plus récent en premier. Il les transmet ensuite à un fichier modèle appelé comments.html, que vous créerez plus tard :

flacon_app/app.py

# ...

@app.route('/comments/')
def comments():
    comments = Comment.query.order_by(Comment.id.desc()).all()
    return render_template('comments.html', comments=comments)

Enregistrez et fermez le fichier.

Vous utilisez la méthode order_by() sur l'attribut query pour récupérer tous les commentaires dans un ordre spécifique. Dans ce cas, vous utilisez la méthode desc() sur la colonne Comment.id pour récupérer les commentaires dans l'ordre décroissant, les derniers commentaires étant les premiers. Ensuite, vous utilisez la méthode all() pour obtenir le résultat et l'enregistrer dans une variable appelée comments.

Vous rendez un modèle appelé comments.html, en lui passant l'objet comments qui contient tous les commentaires classés par le plus récent en premier.

Ouvrez ce nouveau fichier modèle comments.html :

nano templates/comments.html

Tapez le code suivant à l'intérieur. Cela affichera les commentaires et le lien vers le message auquel ils appartiennent :

flask_app/templates/comments.html

{% extends 'base.html' %}

{% block content %}
    <span class="title"><h1>{% block title %} Latest Comments {% endblock %}</h1></span>
    <div class="content">
                {% for comment in comments %}
                    <div class="comment">
                        <i>
                            (#{{ comment.id }})
                            <p>{{ comment.content }}</p>
                        </i>
                        <p class="title">
                        On <a href="{{ url_for('post',
                                                post_id=comment.post.id) }}">
                                {{ comment.post.title }}
                              </a>
                        </p>
                    </div>
                {% endfor %}
            </div>
    </div>
{% endblock %}

Enregistrez et fermez le fichier.

Ici, vous étendez le modèle de base, définissez un titre et parcourez les commentaires à l'aide d'une boucle for. Vous affichez l'ID du commentaire, son contenu et un lien vers le message auquel il appartient. Vous accédez aux données de publication via comment.post.

Utilisez votre navigateur pour accéder à la page des commentaires :

http://127.0.0.1:5000/comments/

Vous verrez une page semblable à la suivante :

Modifiez maintenant le modèle base.html pour que le lien de la barre de navigation Comments pointe vers cette page de commentaires :

nano templates/base.html

Modifiez la barre de navigation pour qu'elle se présente comme suit :

flask_app/templates/base.html

    <nav>
        <a href="{{ url_for('index') }}">FlaskApp</a>
        <a href="{{ url_for('comments') }}">Comments</a>
        <a href="#">About</a>
    </nav>

Enregistrez et fermez le fichier.

Actualisez votre page de commentaires et vous verrez que le lien de la barre de navigation Commentaires fonctionne.

Vous avez maintenant une page qui affiche tous les commentaires de la base de données. Ensuite, vous ajouterez un bouton sous chaque commentaire sur la page de publication pour permettre aux utilisateurs de le supprimer.

Étape 7 - Suppression de commentaires

Dans cette étape, vous allez ajouter un bouton Supprimer le commentaire sous chaque commentaire pour permettre aux utilisateurs de supprimer les commentaires indésirables.

Tout d'abord, vous allez ajouter une nouvelle route /comments/ID/delete qui accepte les requêtes POST. La fonction d'affichage recevra l'ID du commentaire que vous souhaitez supprimer, le récupérera dans la base de données, le supprimera et la redirection vers la page de publication sur laquelle se trouvait le commentaire supprimé.

Ouvrez app.py :

nano app.py

Ajoutez la route suivante à la fin du fichier.

flacon_app/app.py

# ...

@app.post('/comments/<int:comment_id>/delete')
def delete_comment(comment_id):
    comment = Comment.query.get_or_404(comment_id)
    post_id = comment.post.id
    db.session.delete(comment)
    db.session.commit()
    return redirect(url_for('post', post_id=post_id))

Enregistrez et fermez le fichier.

Ici, au lieu d'utiliser le décorateur app.route habituel, vous utilisez le décorateur app.post introduit dans Flask version 2.0.0, qui a ajouté des raccourcis pour les méthodes HTTP courantes. Par exemple, @app.post("/login") est un raccourci pour @app.route("/login", methods=["POST"]). Cela signifie que cette fonction d'affichage n'accepte que les requêtes POST, et la navigation vers la route /comments/ID/delete sur votre navigateur renverra une erreur 405 Method Not Allowed, car les navigateurs Web utilisent par défaut les requêtes GET. Pour supprimer un commentaire, l'utilisateur clique sur un bouton qui envoie une requête POST à cette route.

Cette fonction de visualisation delete_comment() reçoit l'ID du commentaire à supprimer via la variable URL comment_id. Vous utilisez la méthode get_or_404() pour obtenir un commentaire et l'enregistrer dans une variable comment, ou répondez avec un 404 Not Found si le commentaire n'existe pas. Vous enregistrez l'ID de publication de la publication à laquelle appartient le commentaire dans une variable post_id, que vous utiliserez pour rediriger vers la publication après la suppression du commentaire.

Vous utilisez la méthode delete() sur la session de base de données dans la ligne db.session.delete(comment), en lui passant l'objet de commentaire. Cela configure la session pour supprimer le commentaire chaque fois que la transaction est validée. Comme vous n'avez pas besoin d'effectuer d'autres modifications, vous validez directement la transaction en utilisant db.session.commit(). Enfin, vous redirigez l'utilisateur vers la publication sur laquelle le commentaire maintenant supprimé a été publié.

Modifiez ensuite le modèle post.html pour ajouter un bouton Supprimer le commentaire sous chaque commentaire :

nano templates/post.html

Modifiez la boucle for en ajoutant une nouvelle balise <form> directement sous le contenu du commentaire :

flask_app/templates/post.html

    {% for comment in post.comments %}
        <div class="comment">
            <p>#{{ comment.id }}</p>
            <p>{{ comment.content }}</p>
            <form method="POST"
                action="{{ url_for('delete_comment',
                                    comment_id=comment.id) }}">
                <input type="submit" value="Delete Comment"
                    onclick="return confirm('Are you sure you want to delete this entry?')">
            </form>
        </div>
    {% endfor %}

Enregistrez et fermez le fichier.

Ici, vous avez un formulaire Web qui soumet une requête POST à la fonction d'affichage delete_comment(). Vous passez comment.id en argument du paramètre comment_id pour spécifier le commentaire qui sera supprimé. Vous utilisez la fonction confirm() method disponible dans les navigateurs Web pour afficher un message de confirmation avant de soumettre la requête.

Accédez maintenant à une page de publication sur votre navigateur :

http://127.0.0.1:5000/2/

Vous verrez un bouton Supprimer le commentaire sous chaque commentaire. Cliquez dessus et confirmez la suppression. Vous verrez que le commentaire a été supprimé.

Vous avez maintenant un moyen de supprimer des commentaires de la base de données.

Conclusion

Vous avez créé un petit système de blogs qui montre comment gérer les relations un-à-plusieurs à l'aide de l'extension Flask-SQLAlchemy. Vous avez appris à connecter une table parent à une table enfant, à associer un objet enfant à son parent et à l'ajouter à la base de données, et à accéder aux données enfant à partir d'une entrée parent et vice versa.

Si vous souhaitez en savoir plus sur Flask, consultez les autres didacticiels de la série Comment créer des applications Web avec Flask.