#Innovatium

0 Abonnés · 8 Publications

Nous sommes une entreprise technologique authentiquement brésilienne, dotée d'une équipe qualifiée, pluridisciplinaire et agile. Nous offrons des solutions efficaces pour l'intégration de systèmes, l'externalisation, l'interopérabilité stratégique, le développement d'applications, l'assistance et les services de conseil en technologies de l'information.
Nous comptons plus de 1 500 projets réalisés et plus de 100 clients au Brésil et en Amérique latine, dans les segments les plus divers : soins de santé, commerce de détail, industrie, services et autres.
Vous souhaitez mieux connaître nos solutions ? Cliquez ici et contactez notre équipe !

Article Iryna Mykhailova · Mars 7, 2024 8m read

Pourquoi connecter Flask à InterSystems IRIS ?

    La première chose qui nous vient à l'esprit lorsque nous envisageons de coupler Flask avec IRIS est un portail permettant d'interagir avec vos clients et partenaires. Un site web où les patients peuvent accéder à leurs examens cliniques en est un bon exemple. Bien entendu, ce cas nécessiterait une toute nouvelle couche de sécurité, que nous n'avons pas abordée dans notre dernier article. Cependant, nous pouvons l'ajouter sans effort avec Werkzeug, par exemple.
Les sites Web qui établissent un lien entre vos produits et vos clients sont, en effet, un excellent exemple de ce que vous pouvez réaliser en connectant ces technologies car Flask est flexible et vous pouvez facilement ajouter et modifier n'importe quelle fonctionnalité avec cette application ou même faire plus de choses si vous avez une certaine expérience avec CI/CD.
Examinons quelques-unes des astuces que vous pouvez réaliser. Si vous vous souvenez de la première application présentée initialement, vous vous rappellerez que Flask n'a pas besoin d'une structure complexe pour fonctionner, ce qui signifie que vous pouvez le construire à partir de zéro en quelques minutes de codage seulement. Ainsi, vous pouvez rapidement créer de multiples interfaces conviviales et des portails interactifs pour afficher et analyser les données. Par exemple, vous pouvez suivre séparément chaque élément d'une production. Vous pouvez également fournir toutes les fonctionnalités d'InterSystems IRIS que vous utilisez fréquemment sur une magnifique plate-forme sans code. 
    Je travaillais pour Innovatium depuis l'année dernière, et cette expérience m'a permis de rencontrer de nombreuses situations où il était utile de prendre en compte la mémoire utilisée dans les ensembles de résultats de requêtes, les requêtes et les tables, de temps en temps. Ce problème est facile à résoudre en créant un portail similaire à celui créé dans mon article précédent sur Django. Comme le développement avec Flask est plus rapide qu'avec Django, il serait facile de construire quelques plateformes différentes pour chaque projet et pour chaque table que nous souhaitons suivre. En outre, si vous avez un peu de temps libre, vous pouvez automatiser votre plateforme pour obtenir des mises à jour en direct avec Flask-SocktetIO.
Avoir un modèle pour démarrer vos projets peut considérablement accélérer le développement. Pour cette raison, nous allons présenter dans la section suivante un modèle de CRUD de Flask-IRIS publié sur OpenExchange (celui que nous avons construit dans la première partie de cette série d'articles). Autrement dit, si vous n'avez pas le temps d'étudier tous les détails abordés dans le dernier article, il vous suffit de télécharger l'application et de continuer à travailler en toute tranquillité sur votre projet, en vous appuyant sur les connaissances que vous avez déjà acquises.
 

L'application OpenExchange

J'ai décidé d'écrire cette série d'articles en m'inspirant de l'idée mentionnée dans l'article sur l'exemple d'application Flask avec SQLAlchemy Example of Flask application with SQLAlchemy IRIS. Cependant, puisque vous êtes déjà peut-être familier avec les frameworks web de Python et la logique de CRUD, je vais vous présenter une application qui vous permettra de prendre le taureau par les cornes. Ainsi, vous ne perdrez pas votre temps à lire avant la concrétisation de votre projet.
    Suivez les étapes ci-dessous pour télécharger et lancer l'application, commencez à l'utiliser et constatez à quel point elle est facile à mettre en œuvre. Il existe une version plus succincte du tutoriel très proche sur son utilisation sur le fichier README du référentiel GitHub associé. Vous pouvez également consulter tous les liens relatifs à l'application sur sa page Open Exchange.

Guide d'installation

    Avant de développer des projets avec Python, il est toujours préférable de configurer un environnement virtuel. Cela vous aidera à ajouter toutes les exigences, configurations et variables spécifiques à ce projet, sans affecter votre ordinateur et d'autres projets.
    Si vous utilisez Windows/Linux/macOS, ouvrez le terminal dans le répertoire où vous souhaitez démarrer votre projet et créez un environnement virtuel à l'aide de la commande suivante:
python -m venv .venv-folder
    La commande susmentionnée peut être décomposée comme suit : "python" définira un environnement Python pour les commandes suivantes, le drapeau -m lancera un module (dans ce cas, venv), et .venv-folder créera un dossier appelé venv-folder dans le répertoire courant (référencé par le point), où le module venv s'exécutera.
    La prochaine étape consistera à activer l'environnement créé avant de télécharger les prérequis et de lancer votre projet dans les règles de l'art.
    Si vous utilisez macOS ou Linux, vous devez taper “. .venv/bin/activate”. Pour Windows, la commande similaire “.venv\Scritps\activate” lancera le fichier approprié pour la tâche.
    Ensuite, vous pouvez cloner le référentiel avec la commande git clone.
git clone https://github.com/heloisatambara/flask-iris.git
    Enfin, installez la configuration requise à l'aide du paquet d'installation "Python Install Package", et vous serez prêt à commencer le codage.
pip install -r requirements.txt
    Comme vous pouvez le vérifier dans le fichier référencé, la commande ci-dessus installera au moins la version 2.3.3 de Flask, au moins la version 3.1.1 de Flask-SQLAlchemy, et au moins la version 0.10.5 de sqlalchemy-iris, par ^CaretDev.

Guide d'utilisation

    Tout d'abord, il faut connecter l'application à la base de données et à l'espace de noms souhaités. Comme vous avez sans doute appris dans l'article précédent, cette connexion se fait sur le fichier flaskr-iris/database.py, au format “iris://username:password@host:port/NAMESPACE”, selon les spécifications de la documentation SQLALchemy documentation pour la fonction create_engine(). Examinez l'exemple ci-dessous.
    engine = create_engine("iris://_SYSTEM:sys@localhost:1972/SAMPLE")
    Vous pouvez maintenant exécuter l'application web et constater que l'exemple développé fonctionne, après avoir vérifié que l'instance est en cours d'exécution, à l'aide de la commande mentionnée.
...\flask-iris> flask --app flaskr-iris run --debug
    Pour adapter l'application à vos besoins, commencez par modifier le fichier flaskr-iris/models.py pour refléter les données à transférer d'IRIS à Python. L'exemple cloné sur GitHub couvre déjà un grand nombre de cas, comme vous pouvez le vérifier dans l'article précédent, dans la section dédiée aux modèles. Si vous avez besoin d'autre chose, vous pouvez consulter la documentation Flask-SQLAlchemy ou SQLAlchemy oncernant respectivement la définition de modèles et les tables déclaratives.
    Ensuite, il est recommandé de poursuivre le développement en modifiant les fichiers dans flaskr-iris/templates, afin d'adapter l'affichage à vos données. La documentation de Jinja documentation contient toutes les options pour coder vos modèles.
    Ensuite, vous pouvez utiliser les exemples de auth.py et blog.py pour créer vos vues. N'oubliez pas d'enregistrer tous les nouveaux Blueprints dans la fabrique create_app(), dans le fichier __init__.py.
    Enfin, avec les fichiers à l'intérieur du dossier static, vous devriez gagner une longueur d'avance pour ajouter une touche de votre marque à la page web.
 

Section supplémentaire - Les erreurs que j'ai rencontrées et la façon de les corriger

  • Commençons par ce qui peut sembler être une erreur de débutant mais qui peut arriver à tout le monde : si vous avez oublié d'initialiser l'instance IRIS à laquelle vous vous connectez, vous risquez de recevoir le message suivant :
sqlalchemy.exc.OperationalError: (intersystems_iris.dbapi._DBAPI.OperationalError) [WinError 10061] No connection could be made because the target machine actively refused it.


Bien sûr, tout ce que vous avez à faire ici est d'appuyer sur " Start InterSystems IRIS " (démarrage d'InterSystems IRIS) et d'exécuter à nouveau votre application Flask.
 

  • L'erreur suivante peut sembler un peu plus difficile à résoudre :
RuntimeError: Working outside of request context.

This typically means that you attempted to use functionality that needed an active HTTP request. Consult the documentation on testing for information about how to avoid this problem.

db.init_app(app)

    Plusieurs raisons peuvent provoquer ce type d'erreur. Si vous l'avez trouvé lors d'un test, utilisez test_client pour simuler une requête complète. Sinon, il vous faudra peut-être déplacer votre code dans une vue. Cependant, j'ai rencontré cette erreur dans une situation peu courante : Je n'avais pas initié la base de données avec l'application Flask. Pour y remédier, lorsque vous définissez votre base de données avec db = SQLAlchemy() ou quelque chose de similaire, vous pouvez vous connecter directement à l'application en faisant ce qui suit :

app = Flask(__name__)
db = SQLAlchemy(app)

Par ailleurs, si vous travaillez avec une fabrique d'applications, comme indiqué dans l'article précédent, vous pouvez connecter l'application plus tard en appelant init_app() :

db = SQLAlchemy()

defcreate_app(): app = Flask(name) db.init_app(app)

  • Si vous choisissez de suivre l'approche décrite dans la première partie de cet article, vous remarquerez peut-être qu'à chaque fois que vous exécutez votre application, celle-ci essayera de créer toutes les bases de données à partir des modèles que vous avez définis. Cela signifie que vous devrez peut-être traiter ce type d'erreur avec un bloc "try/except". Il est également possible d'ignorer l'erreur si la table existe déjà, mais il faut aussi régler d'autres problèmes. Toutefois, si vous choisissez cette solution, vous devrez peut-être supprimer toutes les tables manuellement chaque fois que vous y apporterez des modifications.  Une autre option consiste à utiliser l'attribut "checkfirst" lors de l'appel de la fonction create_all(). Cela permet d'ignorer les tables qui existent déjà, de sorte que les changements ne seront pas appliqués. Si cela correspond à vos besoins, profitez-en. Pour une mise en œuvre à plus grande échelle, vous pouvez utiliser SQLAlchemy afin de supprimer les tables précédemment créées avant d'exécuter create_all(). Attention : les tables seront créées automatiquement sous le format de nom DefaultSchema_snake_case_model_name. Pour illustrer cette idée, dans l'exemple que nous venons d'explorer les tables ont été construites dans IRIS sous les noms de SQLUser_user et SQLUser_post.
try:
with app.app_context():
db.create_all()
except DatabaseError as err:
if 'already exists' in err._sql_message():
print("Databases already exist.")
else:
print(err) 
  • Enfin, si vous placez les modèles dans un autre fichier par rapport à celui que vous avez utilisé pour exécuter la fonction create_all(), vous pouvez avoir l'impression que cette fonction échoue. Cela se produit parce les modèles que vous voulez assembler doivent être importés avant d'essayer de les fabriquer.
from .models import User, Post
db.init_app(app)
try:
with app.app_context():
db.create_all()
0
0 75
Article Iryna Mykhailova · Mars 4, 2024 24m read

Index

Partie 1

  • Présentation de Flask : une revue rapide des documents de Flask (Flask Docs), où vous trouverez toutes les informations dont vous avez besoin pour ce tutoriel;
  • Connexion à InterSystems IRIS : un guide détaillé étape par étape sur l'utilisation de SQLAlchemy pour se connecter à une instance d'IRIS;

Partie 2

  • Discussion sur cette forme de mise en œuvre : pourquoi nous devrions l'utiliser et les situations où elle est applicable.
  • L'application OpenExchange : si vous n'êtes pas intéressé par le développement de cet exemple, vous pouvez passer directement à cette section et apprendre à l'utiliser comme modèle pour votre application flask-iris.
  • Bonus : quelques erreurs commises lors du développement et comment je les ai résolues.

Présentation de Flask

Flask est un outil web écrit en Python et conçu pour vous faciliter la vie lors de la création d'une application web. Il utilise Werkzeug pour gérer l'interface Web Server Gateway Interface (WSGI) - la spécification standard de Python pour la communication avec les serveurs web. Il utilise également Jinja pour gérer les modèles HTML, et le Click CLI toolkit pour créer et gérer le code d'interface de ligne de commande.

Installation

La première étape est facultative. Cependant, c'est une bonne idée de configurer un environnement virtuel avant de commencer des projets. Vous pouvez le faire sur le terminal en modifiant le répertoire vers le dossier que vous voulez utiliser comme racine et en tapant la commande mentionnée ci-dessous.

> python -m venv .venv

Si vous optez pour un environnement virtuel, activez-le avec la commande suivante (sous Windows) avant d'installer les prérequis :

> .venv\Scripts\activate

Enfin, installez les paquets avec pip (paquetage d'installation Python).

> pip install Flask
> pip install Flask-SQLAlchemy
> pip install sqlalchemy-iris

Quickstart

Nous suivrons la structure présentée dans ce Tutoriel, en adaptant les connexions à l'IRIS. Cela minimisera vos problèmes lors de l'amélioration de votre application puisque vous pourrez consulter la Documentation de Flask sans conflits. Cependant, comme Flask est très flexible, il ne vous oblige pas à suivre des modèles, et vous pouvez considérer les étapes suivantes comme de simples suggestions.

L'approche présentée ici consiste à utiliser les applications comme des paquets et à définir une fabrique d'applications pour couvrir les grands projets. Cependant, vous pouvez toujours construire l'application Flask avec cinq lignes de code seulement, comme on le voit ci-dessous.

from flask import Flask

app = Flask(__name__)


@app.route('/')defhello():return'Hello, world!'

 

Une application modèle

Cette section présente les étapes à suivre pour créer l'application flask-iris. Ce tutoriel peut être suivi si vous n'avez jamais utilisé de framework web auparavant. Cependant, si vous voulez vraiment comprendre la théorie de ce type de flux de travail, lisez mon article précédent sur Exemples de travail avec IRIS à partir de Django, en prenant particulièrement en compte l'image au début de l'article. Vous constaterez avec satisfaction que les différents frameworks web ont une logique très similaire. Cependant, dans Flask, nous pouvons définir les URL et les vues ensemble en tant que groupes, appelés plans originaux (blueprints).

TLes conditions requises concernent Flask, Flask-SQLAlchemy et sqlalchemy-iris. Intuitivement, le pont entre l'application web et votre instance IRIS est construit !
 

Le paquet

Tout d'abord, créez ou sélectionnez le dossier que vous souhaitez utiliser comme racine. Appelons-le flaskr-iris. Nous avons besoin que Python comprenne ce dossier comme un paquet, nous devons donc créer un fichier __init_.py. C'est là que nous placerons notre fabrique d'applications.

Étape 1 - Création de la fabrique d'applications

La fabrique d'applications n'est rien de plus qu'une fonction. A l'intérieur de celle-ci, nous devons définir une instance Flask() (l'application), définir ses configurations, la connecter à la base de données, et enregistrer les blueprints si nous choisissons de les utiliser. Nous allons explorer plus en détail les blueprints plus tard, donc pour l'instant vous n'avez pas besoin de vous en préoccuper.

Cette fonction doit être appelée create_app(). Commencez par importer Flask et créez une instance avec l'argument \N_nom\N_ (consultez la documentation pour plus de détails). Ensuite, mappez la propriété config avec une clé secrète et l'URI de la base de données. Utilisez le format "dialect://username:password@host:port/NAMESPACE", en suivant les recommandations de SQLAlchemy. Dans ce cas, le dialecte devrait être 'iris', créé par CaretDev. Le nom d'utilisateur et le mot de passe sont ceux que vous utilisez pour vous connecter à l'instance InterSystems IRIS indiquée par l'hôte et le port, et l'espace de noms parle de lui-même.

# __init__.pyfrom flask import Flask

defcreate_app():# créer et configurer l'application
    app - Flask(__name__, instance_relative_config=True)

    app.config.from_mapping(
        SECRET_KEY = "dev", # remplacer celle-ci par une clé de génération aléatoire lors du déploiement
        SQLALCHEMY_DATABSE_URI = "iris://_SYSTEM:sys@loclhost:1972/SAMPLE"
    )

Les prochaines lignes de notre fabrique auront besoin de la base de données, alors réservons ce fichier pour un instant pendant que nous parlons de données et de modèles.
 

Étape 2 - Gestion des données

Nous utiliserons SQLAlchemy pour importer et gérer les données puisque nous pouvons utiliser Flask-SQLAlchemy et SQLAlchemy-IRIS pour résoudre les problèmes de connexion. Tout d'abord, il faut créer le fichier database.py dans lequel nous importerons la version Flask de SQLAlchemy et l'instancierons. Cette étape ne nécessite pas de fichier séparé. Cependant, elle peut être très utile plus tard pour développer d'autres méthodes pour gérer la base de données.

# database.pyfrom flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

Ensuite, il faut définir les modèles qui indiqueront à Python comment interpréter chaque enregistrement dans un tableau. Créez le fichier models.py, importez l'instance de SQLAlchemy et les fonctionnalités dont vous avez besoin pour traiter les données. En suivant l'exemple donné dans la documentation officielle de Flask, nous pouvons créer un blog nécessitant un modèle pour les utilisateurs et un autre pour les messages. Cette implémentation couvre un nombre substantiel de cas et vous donne une bonne longueur d'avance.

Nous allons utiliser le Mapping Objet-Relationnel (ORM) comme suggéré dans SQLAlchemy 2.0, en définissant des colonnes mappées et une relation de type Un-à-plusieurs (One-to-many). Vous pouvez vérifier comment modéliser d'autres types de relations dans le Guide de SQLAlchemy.

# models.pyfrom .database import db
from sqlalchemy.orm import Mapped, mapped_column
from typing import List, Optional
import datetime

classUser(db.Model):
    id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
    username: Mapped[str] = mapped_column(db.String(1000), unique=True, nullable=False)
    password: Mapped[str] = mapped_column(db.String(1000), nullable=False)
    posts: Mapped[List["Post"]] =  db.relationship(back_populates="user")

classPost(db.Model):
    id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
    author_id: Mapped[int] = mapped_column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    user: Mapped["User"] = db.relationship(back_populates="posts")
    created: Mapped[datetime.datetime] = mapped_column(db.DateTime, nullable=False, server_default=db.text('CURRENT_TIMESTAMP'))
    title: Mapped[str] = mapped_column(db.String(1000), nullable=False)
    body: Mapped[Optional[str]] = mapped_column(db.String(1000000))

Le format général à utiliser pour définir les colonnes dans la dernière version de SQLAlchemy est le suivant :

columnName: Mapped[type] = mapped_column(db.Type, arguments)

En ce qui concerne les relations, du côté des "nombreux", vous devrez également définir la clé étrangère.

Obs.: les noms des classes seront convertis de "CamelCase" en "snake_case" lors de la création des tableaux SQL.

Enfin, nous pouvons retourner à la fabrique sur _init_.py_ et faire les connexions. Importez la base de données et les modèles, assignez cette application à l'instance db, et créez toutes les tableaux à l'intérieur de la méthode create_app().

# __init__.pyfrom flask import Flask
from sqlalchemy.exc import DatabaseError

defcreate_app():# créer et configurer l'application
    app = Flask(__name__)

    app.config.from_mapping(
        SECRET_KEY="dev", # remplacer par un système aléatoire lors du déploiement
        SQLALCHEMY_DATABASE_URI = "iris://_SYSTEM:sys@localhost:1972/SAMPLE"
    )

    # avec cette application, flask initialise Alchemyfrom .database import db
    from .models import User, Post

    db.init_app(app)

    try:
        with app.app_context():
            db.create_all()
    except DatabaseError as err:
        if'already exists'in err._sql_message():
            print("Databases already exist.")
        else:
            print(err)

Ensuite, nous passerons à la création des vues.

Étape 3 - Les vues

Étape 3.1 - Les blueprints : l'authentification

Un objet Blueprint a été conçu pour organiser vos vues en groupes et les enregistrer dans l'application. Nous allons créer un Blueprint pour les vues qui traitent de l'authentification et un autre pour l'affichage, l'édition et la suppression, de sorte que cette partie du tutoriel couvrira la gestion CRUD et la gestion des sessions.
En commençant par le Blueprint d'authentification, créez le fichier auth.py et importez les utilitaires de Flask et Werkzeug, la base de données et le modèle Utilisateur. Ensuite, instanciez l'objet Blueprint, en spécifiant son nom et le préfixe de l'URL.

# auth.pyfrom flask import (
    Blueprint, request, g, redirect, url_for, flash, render_template, session
)
from werkzeug.security import generate_password_hash, check_password_hash
from .database import db
from .models import User
import functools

bp = Blueprint('auth', __name__, url_prefix='/auth')

Ensuite, nous pouvons utiliser des décorateurs pour définir des chemins pour l'enregistrement, la connexion et la déconnexion avec toutes les méthodes nécessaires. Dans notre exemple, nous utiliserons GET et POST et commencerons par l'enregistrement.

Le décorateur est accessible comme une méthode de notre Blueprint et possède un argument pour l'URL et un autre pour les méthodes qu'il accepte. À la ligne suivante, créez la fonction indiquant à l'application ce qu'elle doit faire lorsqu'elle reçoit une requête avec les paramètres correspondants. Dans l'exemple suivant, nous aurons accès à la fonction en GETtant ou POSTant l'URL http://host:port/auth/register.
 

# auth.py@bp.route('/register', methods=('GET', 'POST'))defregister():if request.method=='POST':
        pass

Si l'application reçoit un POST renvoyant à '/register', cela signifie qu'un utilisateur souhaite créer un compte. L'accès au nom d'utilisateur et au mot de passe choisis avec l'objet de contenu de la demande est géré par Flask. Ensuite, créez une instance du modèle Utilisateur, en stockant ses propriétés en tant que valeurs reçues. Vous pouvez également utiliser les méthodes werkzeug.security pour protéger le contenu sensible. Ensuite, utilisez la propriété de session de notre base de données, fournie par SQLAlchemy, pour ajouter un nouvel utilisateur au tableau et le valider. À ce stade, les informations ont déjà été envoyées au tableau correspondant dans l'instance IRIS mentionnée à la dernière étape. Enfin, redirigez l'utilisateur vers la page de connexion, que nous créerons à l'étape suivante. N'oubliez pas de traiter les erreurs telles que la réception d'entrées vides, l'intégrité de l'index et la connexion à la base de données. Vous pouvez utiliser flash() pour afficher les détails du problème à l'utilisateur. Nous pouvons également utiliser la même fonction register() pour rendre le modèle de la page d'enregistrement si GET est utilisé dans la requête.

# auth.py@bp.route('/register', methods=('GET', 'POST'))defregister():if request.method=='POST':
        username = request.form['username']
        password = request.form['password']
        error = Noneifnot username:
            error = "Username is required."elifnot password:
            error = "Password is required."if error isNone:
            try:
                user = User(
                    username=username,
                    password=generate_password_hash(password)
                )

                db.session.add(user)
                db.session.commit()
            except Exception as err: # TODO: remplacer par l'équivalent db.IntegrityError de sqlite3
                error = str(err)
            else:
                return redirect(url_for("auth.login"))

        flash(error)

    return render_template('auth/register.html')

Ensuite, nous répétons la même logique pour la page de connexion. Lorsque nous traitons un POST, nous recevons d'abord un nom d'utilisateur et un mot de passe, puis nous vérifions s'ils existent dans notre tableau IRIS. Cette fois, nous n'utiliserons pas la propriété session (bien que nous aurions pu le faire). Nous utiliserons plutôt la méthode one_or_404(). Nous devons le faire parce que le nom d'utilisateur doit être unique, comme nous l'avons défini dans notre modèle. Ainsi, si la requête ne renvoie pas précisément une ligne suite à notre demande, nous pouvons la considérer comme non trouvée. À l'intérieur de cette fonction, enchaîner des commandes SQL pour trouver le résultat requis, en utilisant les modèles comme des tableaux et leurs propriétés comme des colonnes. Enfin, on efface la session de Flask, on ajoute l'utilisateur si la connexion a été effective et on le redirige vers la page d'accueil. S'il s'agit d'un GET, envoyer l'utilisateur à la page de connexion.

#auth.py@bp.route('/login', methods=('GET', 'POST'))deflogin():if request.method=="POST":
        username = request.form['username']
        password = request.form['password']
        error = Nonetry:
            user = db.one_or_404(db.select(User).where(User.username==username))

            ifnot check_password_hash(user.password, password):
                error = "Incorrect password"except Exception as err: # TODO vérifier également cette erreur
            error = f"User {username} not found."if error isNone:
            session.clear()
            session['user_id'] = user.id
            return redirect(url_for('index'))

        flash(error)


    return render_template('auth/login.html')
    

Pour se déconnecter, il suffit d'effacer la session et de rediriger vers la page d'accueil. 

# auth.py@bp.route('/logout')deflogout():
    session.clear()
    return redirect(url_for('index'))

Pour les fonctions suivantes, nous allons traiter l'utilisateur qui se connecte sur différentes requêtes, qui ne sont pas nécessairement liées à l'authentification. L'objet 'g' de Flask est unique pour chaque demande, ce qui signifie que vous pouvez l'utiliser pour définir l'utilisateur actuel à partir de la base de données lorsque vous traitez des accès éventuellement interdits.

Heureusement, le Blueprint possède la propriété before_app_request. Elle peut être utilisée comme décorateur suivi d'une fonction pour déterminer ce qu'il convient de faire lors de la réception d'une requête avant de la traiter. Pour charger l'utilisateur connecté, nous devons à nouveau nous connecter, mais cette fois, nous obtiendrons des informations à partir de la session autres que la requête.

# auth.py@bp.before_app_requestdefload_logged_in_user():
    user_id = session.get('user_id')

    if user_id isNone:
        g.user = Noneelse:
        g.user = db.one_or_404(
            db.select(User).where(User.id==user_id)
        )

Enfin, pour notre dernière fonction d'authentification, il est possible de créer quelque chose qui traitera les vues qui doivent être interdites si l'utilisateur ne s'est pas connecté. Si vous ne voulez pas afficher un simple code 403, redirigez l'utilisateur vers la page de connexion.

# auth.pydeflogin_required(view):    @functools.wraps(view)defwrapped_view(**kwargs):if session.get('user_id') isNone:
            return redirect(url_for('auth.login'))

        return view(**kwargs)

    return wrapped_view

Étape 3.2 - Les blueprints: blog (CRUD)

Là encore, commencez par créer le fichier (appelons-le blog.py) et y importer tout ce qui est nécessaire. Cela peut sembler beaucoup de travail, mais chacune de ces importations prendra tout son sens au fur et à mesure que nous avancerons. Créez également une autre instance de Blueprint.

# blog.pyfrom flask import (
    Blueprint, flash, g, redirect, render_template, request, url_for, session
)
from werkzeug.exceptions import abort
from .auth import login_required
from .database import db
from .models import Post, User
from sqlalchemy import desc

bp = Blueprint('blog', __name__)

Tout d'abord, il faut commencer par créer l'URL par défaut, qui pointe vers la page d'accueil. Ici, nous afficherons les messages stockés dans IRIS, de sorte que la propriété de session de la base de données (qui est une instance SQLAlchemy() de Flask-SQLAlchemy) puisse être utilisée. Il y a plusieurs façons d'exécuter une requête avec le module SQLAlchemy. J'ai choisi d'utiliser la fonction scalars() avec la requête SQL comme argument, enchaînée avec la fonction all(). Vous avez peut-être vu un comportement similaire dans d'autres langages et plugins comme fetchall(). La prochaine étape sera de rendre le modèle de la page d'accueil, en passant la variable où nous avons stocké tous les messages comme argument afin de pouvoir y accéder dans les fichiers HTML. Regardez notre READ (cRud) ci-dessous :

# blog.py@bp.route('/')defindex():
    posts = db.session.scalars(
        db.select(Post).order_by(desc(Post.created))
    ).all()

    return render_template('blog/index.html', posts=posts)

Ensuite, la fonction de création (Crud) d'un nouveau message sera très similaire à l'enregistrement d'un utilisateur. Nous obtenons l'entrée de l'utilisateur à partir de la requête, définissons ses valeurs en fonction des propriétés du modèle de message Post, utilisons l'objet session de la base de données pour faire un ajout (équivalent à l'instruction SQL d'insertion) et validons. Ici, nous allons accéder à l'utilisateur actuel à partir de la requête avec l'objet g. Nous utiliserons également un second décorateur, référençant la fonction login_required(), pour rediriger l'utilisateur vers la page de connexion s'il ne s'est pas encore connecté.

# blog.py@bp.route('/create', methods=('GET', 'POST'))@login_requireddefcreate():if request.method == 'POST':
        title = request.form['title']
        body = request.form['body']
        error = Noneifnot title:
            error = 'Title is required.'if error isnotNone:
            flash(error)
        else:
            post = Post(
                title=title,
                body=body,
                author_id=g.user.id
            )
            db.session.add(post)
            db.session.commit()

            return redirect(url_for('blog.index'))

    return render_template('blog/create.html')

Pour effectuer une mise à jour (crUd), nous utiliserons une approche très similaire. Cependant, au lieu de créer une nouvelle instance de Post, nous la renverrons à partir d'une requête, puisqu'elle existe déjà dans la base de données. Nous pouvons utiliser l'URL pour recevoir un identifiant pour le message (Post). Une fois de plus, le login est nécessaire ici.

# blog.py@bp.route('//update', methods=('GET', 'POST'))@login_requireddefupdate(id):
    post = get_post(id)

    if request.method == 'POST':
        title = request.form['title']
        body = request.form['body']
        error = Noneifnot title:
            error = 'Title is required.'if error isnotNone:
            flash(error)
        else:
            post.title = title
            post.body = body

            db.session.commit()

            return redirect(url_for('blog.index'))

    return render_template('blog/update.html', post=post)

Puisque pour la suppression il nous faudra également accéder au message (Post) par l'ID, nous pouvons créer une fonction get_post(id) et l'utiliser sur les deux vues. De la même manière que pour les autres requêtes que nous avons faites jusqu'à présent, nous pouvons enchaîner les commandes SQL pour l'écrire. Puisque notre modèle pour l'utilisateur a une relation, nous pouvons sélectionner le message Post et le joindre à Utilisateur (User). De cette manière, nous pourrons accéder aux informations de l'utilisateur par le biais de l'objet résultat. Nous pouvons également utiliser abort() pour envoyer des réponses 404 (non trouvé) et 403 (interdit).

# blog.pydefget_post(id, check_author=True):
    post = db.one_or_404(
        db.select(Post).join(User).where(Post.id == id)
    )

    if post isNone:
        abort(404, f"Post id {id} doesn't exist.")

    if check_author and post.author_id != session['user_id']:
        abort(403)

    return post

Enfin, vous pouvez effectuer la suppression en utilisant simplement une fonction de suppression de la session de la base de données.

# blog.py@bp.route('//delete', methods=('POST',))@login_requireddefdelete(id):
    post = get_post(id)

    db.session.delete(post)
    db.session.commit()

    return redirect(url_for('blog.index'))

 

Étape 3.3 - Enregistrer vos blueprints

Enfin, nous pouvons achever notre fabrique d'applications en enregistrant les blueprints et en renvoyant l'application, comme indiqué ci-dessous.

# __init__.pyfrom flask import Flask
from sqlalchemy.exc import DatabaseError

defcreate_app():# créer et configurer l'application
    app = Flask(__name__)

    app.config.from_mapping(
        SECRET_KEY="dev", # remplacer par un système aléatoire lors du déploiement
        SQLALCHEMY_DATABASE_URI = "iris://_SYSTEM:sys@localhost:1972/SAMPLE"
    )

    # avec cette application, flask initialise Alchemyfrom .database import db
    from .models import User, Post
    db.init_app(app)
    try:
        with app.app_context():
            db.create_all()
    except DatabaseError as err:
        if'already exists'in err._sql_message():
            print("Databases already exist.")
        else:
            print(err)

    # enregistrer les blueprintsfrom . import auth
    app.register_blueprint(auth.bp)

    from . import blog
    app.register_blueprint(blog.bp)
    app.add_url_rule('/', endpoint='index')

    return app

 

Étape 4 - Les modèles

Vous devez avoir remarqué que chaque vue se termine par un retour redirect(), qui envoie l'utilisateur vers une autre vue ou renvoie render_template(), ce qui est explicite. Ces fonctions reçoivent des modèles HTML comme arguments, ainsi que tout autre objet que vous pourriez vouloir utiliser à l'intérieur. Cela signifie qu'à ce stade du tutoriel, vous apprendrez à accéder à votre base de données IRIS à partir d'un fichier HTML, ce qui vous permettra de gérer les données avec CSS et JavaScript, et d'évoluer vers d'autres outils de développement web.

Le chemin par défaut de votre application Flask intégrée dans un paquetage pour rechercher des modèles se trouve dans le dossier app/templates. À l'intérieur de ce dossier, il est d'usage d'ajouter un fichier base.html. Le modèle de ces fichiers est le Jinja's. Cela signifie que vous pouvez utiliser {% ... %} pour ajouter des instructions telles que des blocs, des "ifs" (si) et des "fors" (pour), en les terminant toujours (avec {% endblock %}, par exemple). Vous pouvez également utiliser {{ ... }} pour des expressions telles que l'accès aux objets que vous avez passés dans la fonction de rendu du modèle. Enfin, vous pouvez utiliser {# ... #} pour les commentaires. 

Outre les arguments passés dans les vues, vous pouvez accéder à des contextes tels que l'objet g, qui est unique pour chaque requête, et get_flashed_messages() pour afficher les arguments passés dans flash(). Étant donné que nous avons déjà vu dans les étapes précédentes comment effectuer des requêtes et passer le résultat dans le contexte de la requête, l'exemple suivant pour un fichier de base vous montrera comment accéder aux données d'IRIS en utilisant l'objet g. En plus de cela, l'exemple montrera également comment accéder aux données d'IRIS en utilisant l'objet g. En outre, il montre également comment afficher les messages flashés.
 

<!-- templates/base.html --><!DOCTYPE html><title>{% block title %}{% endblock %} - Flaskr</title><linkrel="stylesheet"href="{{ url_for('static', filename='style.css') }}"><nav><h1>Flaskr</h1><ul>
        {% if g.user %}
          <li><span>{{ g.user['username'] }}</span><li><ahref="{{ url_for('auth.logout') }}">Log Out</a>
        {% else %}
          <li><ahref="{{ url_for('auth.register') }}">Register</a><li><ahref="{{ url_for('auth.login') }}">Log In</a>
        {% endif %}
    </ul></nav><sectionclass="content"><header>
        {% block header %}{% endblock %}
    </header>
    {% for message in get_flashed_messages() %}
      <divclass="flash">{{ message }}</div>
    {% endfor %}
    {% block content %}{% endblock %}
</section>

La base étant maintenant définie, vous pouvez enfin créer d'autres pages en agrandissant ce fichier et en personnalisant les blocs. Explorez l'exemple suivant pour la page Create Posts.

<!-- templates/blog/create.html -->
{% extends 'base.html' %}

{% block header %}
    <h1>{% block title %}New Post{% endblock %}</h1>
{% endblock %}

{% block content %}
    <formmethod="post"><labelfor="title">Title</label><inputname="title"id="title"value="{{ request.form['title'] }}"required><labelfor="body">Body</label><textareaname="body"id="body">{{ request.form['body'] }}</textarea><inputtype="submit"value="Save"></form>
{% endblock %}

Le paramètre "name" (nom) de l'entrée sera la clé du dictionnaire request.form, accessible dans les vues. Les pages d'authentification (login et register) ont une syntaxe assez similaire puisqu'elles ne nécessitent qu'un seul formulaire. Cependant, vous pouvez les consulter sur mon dépôt GitHub si vous le souhaitez.

Maintenant, regardons la page d'accueil, appelée index.html.

<!-- templates/blog/index.html -->
{% extends 'base.html' %}

{% block header %}
 <h1>{% block title %}Posts{% endblock %}</h1>
 {% if g.user %}
    <aclass="action"href="{{ url_for('blog.create') }}">New</a>
 {% endif %}
{% endblock %}

{% block content %}
    {% for post in posts %}
        <articleclass="post"><header><div><h1>{{ post['title'] }}</h1><divclass="about">by {{ post.user.username }} on {{ post['created'].strftime('%Y-%m-%d') }}</div></div>
                {% if g.user['id'] == post['author_id'] %}
                    <aclass="action"href="{{ url_for('blog.update', id=post['id'] )}}">Edit</a>
                {% endif%}
            </header><pclass="body">{{ post['body'] }}</p></article>
        {% if not loop.last %}
            <hr>
        {% endif %}
    {% endfor %}
{% endblock %}

Voici quelques éléments qui valent la peine d'être examinés de plus près. Le lien pour créer un nouveau message n'apparaît que lorsque g.user confirme le vrai, ce qui signifie qu'un utilisateur s'est connecté. Il y a également une instruction "for" (pour), itérant à travers l'objet posts renvoyé par une requête sur la vue pour l'URL vide "" et transmis à ce fichier en tant qu'argument de render_template(). Pour chaque message, nous pouvons accéder au nom d'utilisateur lié à chaque utilisateur avec une syntaxe simple (post.user.username), grâce à la db.relationship ajoutée dans le modèle utilisateur User. Une autre façon d'accéder aux données est d'utiliser les messages comme des dictionnaires et d'afficher leurs propriétés avec une syntaxe d'index (comme post['title']). Portez une attention particulière à la façon dont le lien de mise à jour d'un article prend son ID comme argument et n'apparaît que si son utilisateur correspond à celui qui est actuellement connecté.

Enfin, nous avons la page de mise à jour, qui renvoie simultanément à la vue de suppression. Elle est analogue à la page Créer des messages, mais elle possède une logique particulière pour définir les valeurs par défaut à partir de la base de données. Je laisserai une illustration ci-dessous au cas où vous décideriez de l'explorer.

<!-- templates/blog/udpate.html -->
{% extends 'base.html' %}

{% block header %}
    <h1>{% block title %}Edit "{{ post['title'] }}"{% endblock %}</h1>
{% endblock %}

{% block content %}
    <formmethod="post"><labelfor="title">Title</label><inputname="title"id="title"value="{{ request.form['title'] or post['title'] }}"required><labelfor="body">Body</label><textareaname="body"id="body">{{ request.form['body'] or post['body'] }}</textarea><inputtype="submit"value="Save"></form><hr><formaction="{{ url_for('blog.delete', id=post['id']) }}"method="post"><inputtype="submit"class="danger"value="Delete"onclick="return confirm('Are you sure?');"></form>
{% endblock %}

 

Partie 5 - Ajout de fichiers statiques

Nous disposons à présent d'une interface interactive qui reçoit des informations depuis l'utilisateur jusqu'à la base de données et vice-versa, ainsi que d'objets IRIS accessibles en Python à l'aide de SQLAlchemy et de modèles créés avec la syntaxe des classes. Dans la dernière étape, nous avons également réussi à transformer ces objets en contenu d'éléments HTML, ce qui signifie que vous pouvez facilement les personnaliser et interagir avec eux à l'aide de CSS et de JavaScript.

Si l'on revient à la base HTML, on remarque la ligne contenant un lien renvoyant à l'élément stylesheet, juste au début :

Le premier argument de la fonction url_for() à l'intérieur de l'expression href est le dossier à l'intérieur de la racine où vous pouvez trouver les statiques. Le second, bien sûr, est le nom du fichier. A partir de maintenant, le concept est à vous ! Créez le fichier style.css et modifiez-le à votre guise.

C'est fait !

Avec tous les fichiers que nous avons créés, le dossier racine devrait ressembler à l'image ci-dessous :

Maintenant, rendez-vous dans le répertoire racine du terminal et tapez

flask --app flaskr-iris run --debug

Suivez ensuite le lien http://127.0.0.1:5000 pour voir comment tout fonctionne.  

Conclusion

Dans cet article, nous avons vu comment Flask rend l'apport d'informations depuis une instance IRIS vers une application web possible et facile, en la transformant en un CRUD entièrement personnalisable, avec la possibilité de se connecter à d'autres bases de données et à d'autres systèmes. Désormais, vous pouvez facilement créer, afficher, mettre à jour et supprimer des informations d'une base de données IRIS, via une interface qui vous permet non seulement de personnaliser son apparence, ainsi que de traiter les données avec n'importe quel outil Python sans difficulté.

Vous pouvez me contacter pour me faire part de vos doutes ou des idées que vous avez rencontrées après la lecture ! Dans le prochain article, j'aborderai plus en détail la théorie de cette implémentation et présenterai l'application OpenExchange développée ici. En prime, je révèlerai quelques erreurs que j'ai rencontrées et les solutions que j'ai réussi à trouver dans la dernière section.

0
0 408
Article Sylvain Guilbaud · Fév 16, 2024 2m read

Pourquoi j'ai décidé d'écrire ceci

Dans mon dernier article, j'ai parlé du renvoi de valeurs avec Python. Mais les renvoyer est simple, ce qui peut rendre les choses plus difficiles, c'est ce dont je vais parler aujourd'hui : où la valeur est traitée.

Objet Python dans IRIS

En suivant l'exemple du dernier article, nous avons la méthode : objet dans IRIS

Class python.returnTest [ Abstract ]
{

ClassMethod returnSomething(pValue... As%String) As%Integer [ Language = python ]
{
	return pValue
}

}
0
0 65
Article Lorenzo Scalese · Oct 24, 2023 22m read

Notre objectif

Aujourd'hui, nous poursuivons le développement de notre dernier article et présentons des informations sur certaines fonctionnalités que nous avons ajoutées à notre portail. Nous inclurons une petite partie de CSS pour mieux visualiser les données disponibles et les exporter. Enfin, nous examinerons comment ajouter des options de filtrage et de classement. Lorsque vous aurez terminé cet article, vous pourrez afficher une requête simple et complète de manière élégante.

Précédemment, dans "Un portail pour gérer la mémoire réalisé avec Django"...

Il nous faut reprendre là où nous nous sommes arrêtés avant de passer au développement du portail. Nous avons précédemment créé la base du projet sur la ligne de commande avec certaines commandes intégrées de Django telles que startproject. Ensuite, nous avons ajouté les exigences de connexion à la base de données au fichier requirements.txt et ses paramètres au fichier settings.py pour simplifier l'installation. Plus tard, nous avons créé une application de globales "Globals" avec les URL et les chemins appropriés, dirigeant les utilisateurs du projet principal vers les vues qui communiquent avec les données au moyen de modèles. Nous avons également créé un dossier API avec quelques méthodes qui permettent d'obtenir des informations à partir d'une base de données distante. Avec tout ce qui précède, nous avons laissé à la disposition des utilisateurs une option permettant de créer une communication entre différentes instances d'IRIS. Enfin, nous avons créé le fichier index.html pour l'afficher sur le lien index.html to display it on the link http://127.0.0.1:8000/globals/.

À ce stade, l'interface devraient ressembler à l'image ci-dessous.

Cependant, moins précise que l'illustration précédente, l'interface ressemble à la capture d'écran suivante.

Ajout de CSS

Les feuilles de style en cascade (CSS) permettent de transformer ce projet en quelque chose de plus présentable, de plus beau et de plus agréable à travailler. Dans cette section, vous apprendrez à utiliser les feuilles de style en cascade dans votre projet. Nous vous présenterons également quelques concepts de base de ce langage. Cependant, notre objectif principal sera de relier le fichier de styles et le projet.

Tout d'abord, il faut créer un dossier appelé "static" dans le répertoire /globals. Il sera nécessaire pour y stocker tous nos fichiers statiques par la suite. Vous pouvez ajouter le fichier styles.css à ce dossier.

À présent, nous pouvons enfin mettre de l'ordre et de la couleur. Cependant, si vous ne connaissez rien au CSS, ne vous inquiétez pas ! Voici quelques bonnes nouvelles pour vous ! Pour commencer, c'est relativement intuitif. Il suffit d'écrire l'élément et d'ajouter le design entre les crochets. Vous pouvez accéder aux classes après un point, et aux ID après un hashtag. Si vous voulez un conseil de pro, vous pouvez utiliser un astérisque pour ajouter le style à chaque élément du fichier HTML.

Par exemple, si nous ajoutons un identifiant et une classe aux trois derniers paragraphes du fichier index.html, nous pourrons y accéder séparément et y ajouter des couleurs, des arrière-plans et des polices différents.

<p>showing results for {{globals.count}} globals</p><pclass="totalSize">total size: {{sumSize.size__sum}}</p><pid="allocatedSize">total allocated size: {{sumAllocated.allocatedsize__sum}}</p>
* {
    font-family: 'Lucida Sans';
}

p {
    background-color: yellow;
}

.totalSize {
    color: brown;
}

#allocatedSize {
    font-weight: bold;
}
 

 

Selon cet actif, après avoir configuré les connexions appropriées, chaque police affichée sur la page devrait faire partie de la famille Lucida Sans, les trois paragraphes devraient avoir un arrière-plan jaune, le paragraphe avec la classe _totalSize_ devrait être en caractères bruns, et le paragraphe avec l'ID _allocatedSize_ devrait être en caractères gras.

Vous devriez remarquer quelques particularités si vous jouez avec les éléments, en y ajoutant des ID, des classes et des styles. Premièrement, il y a une hiérarchie : les définitions des identifiants remplacent les classes qui, à leur tour, remplacent les styles généraux. Deuxièmement, plusieurs éléments peuvent partager une même classe. De plus, les éléments enfants hériteront généralement du style de leurs parents (certaines choses ne seront cependant pas héritées, mais il s'agit d'un sujet plus avancé que nous n'aborderons pas ici). Si vous le souhaitez, vous pouvez nous faire part, dans la section des commentaires, des comportements que vous avez remarqués et dont vous n'aviez pas connaissance auparavant.

Ensuite, nous devons faire en sorte que le projet puisse comprendre ce qu'est un fichier de style et où le trouver. Ensuite, nous pouvons obtenir le fichier HTML faisant référence au fichier _styles.css_.

Tout d'abord, accédez à _settings.py_, recherchez la variable STATIC_URL, et modifiez sa valeur de la manière suivante.

STATIC_URL = '/static/'
Cette configuration doit indiquer au projet que tous les fichiers statiques se trouvent dans un dossier appelé "static" à l'intérieur du dossier de l'application. Dans ce cas, il s'agit du répertoire _/globals/static_. Si nous avions une autre application utilisée par ce projet, elle ne reconnaîtrait que son dossier "static" interne (à moins qu'il ne soit spécifié qu'il s'agit d'un dossier général).

Les fichiers statiques sont ceux qui ne changent pas lorsque l'application est en cours d'exécution. Vous pouvez les utiliser pour stocker des fonctions, des définitions, des constantes et des images qui facilitent l'exécution du code (dans ce cas, des fichiers .py). En pratique, c'est dans ces fichiers que vous stockez le JavaScript et le CSS.

L'étape suivante consiste à y faire référence à partir du fichier index.html. Ajoutez une balise

avant le corps. À l'intérieur du corps, utilisez une balise

pour faire référence à une feuille de style de type text/CSS, avec la référence "{% static 'styles.css' %}" et le média "screen". De plus, vous devez indiquer à Django de télécharger les éléments statiques. Rappelez-vous que nous pouvons "dire à Django" quelque chose via le fichier HTML en ajoutant du code entre les crochets et les signes de pourcentage, comme indiqué dans l'exemple : “{% votre code ici %}”.

<head>
    {% load static %}
    <linkrel="stylesheet"type="text/CSS"href="{% static 'styles.css' %}"media="screen"/></head>

J'ai remplacé notre liste de globales par un tableau de globales avec une syntaxe similaire. Ensuite, j'ai ajouté au projet un espacement sympa entre les éléments ainsi que les couleurs de l'Innovatium. Vous pouvez consulter le fichier CSS que j'ai créé sur My GitHub et voir comment ce tableau a pris l'aspect illustré ci-dessous. Je ne suis pas un expert en CSS ou en conception de sites web. Cependant, si vous avez des questions, je serai heureux de les résoudre et même d'apprendre de nouvelles astuces ensemble. Laissez un commentaire ou envoyez-moi un message !

<table><thead><tr><th>Database</th><th>Global</th><th>Size</th><th>Allocated</th></tr></thead><tbody>
        {% for global in globals %}
        <tr><td>{{ global.database }}</td><td>{{ global.name }}</td><td>{{ global.size }}</td><td>{{ global.allocatedsize }}</td></tr>
        {% endfor %}
    </tbody></table>



PS. Si les fichiers statiques sont mis en cache, il est possible que les changements ne soient pas visibles lors du rechargement. Vous pouvez appuyer sur Ctrl+Shift+Delete et supprimer le cache stocké lorsque vous utilisez Google Chrome comme navigateur. Vous devez ensuite recharger votre page encore une fois. Si vous avez tout fait correctement, vous verrez ce qui suit dans votre terminal :

CONSEIL : pour éviter la nécessité de vider le cache à chaque fois que vous utilisez le programme, ajoutez la ligne suivante à l'en-tête de votre fichier HTML : &lt;meta http-equiv="cache-control" content="no-cache" />. Cette balise devrait empêcher la page de stocker le cache.

Exportation de données

Dans cette section, nous revenons au back-end. Il vous sera utile de revoir la partie 1 de cette série d'articles si vous ne connaissez pas le chemin des requêtes (modèle -> URL -> vue -> base de données et retour).
Nous mettrons en place un formulaire permettant à l'utilisateur de choisir entre l'exportation au format CSV, XML ou JSON, car cela couvre la plupart des utilisations pour le transfert de données. Et vous pouvez ajouter tous les langages que vous souhaitez. Pour cela, nous aurons besoin d'un formulaire avec une méthode HTTP, un jeton de sécurité, trois entrées radio et un bouton "Soumettre".

<formmethod="GET">
    {% csrf_token %}
    <inputtype="radio"name="exportLanguage"value="CSV"/><labelfor="CSV">CSV</label><inputtype="radio"name="exportLanguage"value="XML"/><labelfor="XML">XML</label><inputtype="radio"name="exportLanguage"value="JSON"/><labelfor="JSON">JSON</label><buttontype="submit"formaction="{% url 'export' %}"> Export </button></form>

Si vous désirez que les étiquettes affichent un texte "cliquable" pour la valeur correspondante, ajoutez la propriété onclick à l'étiquette avec la valeur getElementById(‘ID correspondant’).checked = true ainsi que les identifiants correspondants pour chaques option. La propriété formaction spécifiée dans le bouton renvoie à l'URL. Ainsi, vous pouvez avoir autant de boutons que vous le souhaitez pour indiquer différentes URL et soumettre le formulaire en conséquence.

Après avoir terminé l'étape précédente, nous pouvons ajouter le chemin qui nous dirige vers la vue sur urls.py et finalement créer la vue sur views.py. Cette vue peut sembler un peu plus complexe que celles que nous avons réalisées précédemment, mais, étape par étape, nous y parviendrons ensemble.

from .views import home, update, export

urlpatterns = [
    path('', home),
    path('update', update, name="update"),
    path('export', export, name="export")
]

Il faut d'abord assigner une variable pour référencer les globales. Ensuite, nous devons créer un chemin où le fichier apparaîtra lorsque l'utilisateur cliquera sur le bouton d'exportation ( il pourra être modifié ultérieurement du côté client). Enfin, nous devons savoir quelle langage a été sélectionnée et procéder à l'exportation adéquate pour chacun d'entre eux.

import os

defexport(request):
    globals = irisGlobal.objects.all()           # gets the globals
    cd = os.getcwd()                             # gets current directory
    language = request.GET.get("exportLanguage") # gets language selected in the formif language == "CSV":
        passelif language == "XML":
        passelif language == "JSON":
        passreturn redirect(home)

Pour les CSV, il suffit de mettre chaque enregistrement sur une ligne et de séparer chaque colonne par une virgule. La manière la plus logique de procéder serait de concaténer toutes les informations relatives à chaque groupe dans une chaîne de caractères entre virgules, suivie d'un terminateur de ligne, et d'écrire chaque ligne dans le fichier.

if language == "CSV":
    with open(cd+"\\test.csv", "w") as file:
        for eachGlobal in globals:
            row = eachGlobal.database+", "+eachGlobal.name+", "+str(eachGlobal.size)+", "+str(eachGlobal.allocatedsize)+"\n"
            file.write(row)
        

 

Pour JSON et XML, les convertisseurs série de Django sont nécessaires. Ils peuvent sembler complexes, mais en réalité, ils sont assez simples. Le module convertisseur série possède deux méthodes: sérialisation and désérialisation, qui permettent de convertir des informations depuis et vers votre langage préféré. Heureusement, XML et JSON sont des options intégrées.

from django.core import serializers

[...]

elif language =="XML": with open(cd+"\test.xml", "w") as file: globals = serializers.serialize("xml", globals) file.write(globals)

elif language =="JSON": with open(cd+"\test.json", "w") as file: globals = serializers.serialize("json", globals) file.write(globals)


Bien joué ! L'application est enfin prête à être rechargée et testée. Après l'exportation, votre espace de travail devrait ressembler à l'image ci-dessous.
 

Filtres

Commençons par appliquer un filtre à une base de données. Nous devons créer une balise de formulaire avec une entrée de texte pour saisir le nom de la base de données et une référence à l'URL. Nous pouvons utiliser l'affichage d'accueil avec quelques adaptations.

<formmethod="GET"action="{% url 'home' %}">
    {% csrf_token %}
    <inputtype="text"name="database"placeholder="Database"/></form>

Puisque nous nous référons maintenant au chemin d'accès à partir de l'index, il faut le nommer sur les modèles, sur urls.py.

path('', home, name="home"),

Rappelez-vous que dans l'affichage d'accueil, nous obtenons toutes les globales du modèle avec irisGlobal.objects.all() et nous les renvoyons à l'index. À ce moment-là, nous ne devons renvoyer qu'un ensemble filtré de ces valeurs globales au lieu de toutes les valeurs. La bonne nouvelle est que nous allons résoudre ce problème avec quatre lignes de code seulement.
    Tout d'abord, comme nous l'avons fait avec l'exportation, nous devons obtenir des informations de l'entrée avec request.GET.get() et réduire notre ensemble de globales en fonction de ce que notre utilisateur souhaite. Grâce à l'objet de requête de Django.db.models, nous pourrons utiliser la fonction de filtrage pour atteindre notre objectif.

from django.db.models import Sum, Q
from .models import irisGlobal

def home(request):
    globals = irisGlobal.objects.all()
    databaseFilter = request.GET.get("database")
    
    if databaseFilter:
        query = Q(database__contains=databaseFilter)
        globals = globals.filter(query)
        

Le Q() est l'objet de requête. Au sein de cet objet, vous pouvez ajouter le nom d'une colonne, deux soulignements et une instruction SQL pour affiner la recherche. Ensuite, vous pouvez passer tous les objets Q nécessaires comme arguments de la fonction de filtrage, et ils seront unis par les opérateurs "AND" (à moins qu'il ne soit spécifié le contraire). Il y a beaucoup d'autres choses que vous pouvez faire avec la classe Q. Pour en savoir plus sur cette classe et sur d'autres façons d'effectuer des requêtes avec Django, consultez la documentation officielle).

Vous pouvez maintenant recharger et tester vos filtres. N'oubliez pas de prêter attention à la façon dont les agrégateurs à la fin de la page s'adaptent aux filtres puisque nous les avons construits à partir de la variable de globales globals .

Plus de filtres !

Si vous êtes à l'aise avec la dernière section, le sujet peut être abordé avec plus d'assurance. Il est temps d'ajouter d'autres filtres et de les combiner. Pour commencer, ajoutez quelques entrées supplémentaires à notre formulaire.

<formmethod="GET">
    {% csrf_token %}
    <inputtype="text"name="database"placeholder="Database"/><inputtype="text"name="global"placeholder="Global name"/><inputtype="number"name="size"placeholder="Size"step="0.001"/><inputtype="number"name="allocated"placeholder="Allocated size"step="0.001"/><buttontype="submit"formaction="{% url 'home' %}"> Filter </button></form>

à l'affichage, ajoutez les nouveaux filtres à la variable globals. Ils doivent s'enchaîner et être équivalents à l'instruction SQL ci-dessous.

    On the view, add the new filters to the variable globals. They should chain and be equivalent to the SQL statement below.

SELECT * FROM globals

WHERE database LIKE ‘%databaseFilter%’ AND

      globals LIKE ‘%globalsFilter%’ AND

      size >=sizeFilter AND

      allocatedsize>=allocFilter

globals = irisGlobal.objects.all()
databaseFilter = request.GET.get("database")
globalFilter = request.GET.get("global")
sizeFilter = request.GET.get("size")
allocFilter = request.GET.get("allocated")

if databaseFilter: globals = globals.filter(Q(database__contains=databaseFilter)) else: databaseFilter=""if globalFilter: globals = globals.filter(Q(name__contains=globalFilter)) else: globalFilter=""if sizeFilter: globals = globals.filter(Q(size__gte=sizeFilter)) else: sizeFilter=""if allocFilter: globals = globals.filter(Q(allocatedsize__gte=allocFilter)) else: allocFilter=""

Le filtre fonctionne déjà, mais il peut encore être quelque peu amélioré. Si nous transmettons les variables reçues au retour, nous pourrons les utiliser comme valeurs dans les entrées. Elles ne disparaîtront pas non plus lorsque nous rechargerons la page ou que nous appuierons sur le bouton filtre.

return render(request, "index.html", {"globals": globals,
    "sumSize": sumSize,
    "sumAllocated":sumAllocated,
    "database":databaseFilter
    "global":globalFilter
    "size":sizeFilter,
    "allocated":allocFilter
})
<formmethod="GET">
    {% csrf_token %}
    <inputtype="text"name="{{database}}"placeholder="Database"/><inputtype="text"name="{{global}}"placeholder="Global name"/><inputtype="number"name="{{size}}"placeholder="Size"step="0.001"/><inputtype="number"name="{{allocated}}"placeholder="Allocated size"step="0.001"/><buttontype="submit"formaction="{% url 'home' %}"> Filter </button></form>

Nous pouvons fournir aux filtres des options pour les mises à jour inférieures ou égales à une valeur spécifique. Nous pouvons même programmer des mises à jour en direct (mise à jour à chaque touche sur laquelle l'utilisateur appuie pendant la saisie des données). Pour l'instant, nous nous contenterons de garder ces idées à l'esprit et nous passerons à l'ajout d'un ordre.

Exportation d'un ensemble filtré

Après tout ce que nous avons fait, il est logique que vous souhaitiez adapter l'affichage de l'exportation pour utiliser tous les filtres que nous avons ajoutés.

Nous pouvons placer toute la logique que nous avons ajoutée à partir des filtres dans une fonction que nous appellerons handle_filters, et au lieu d'utiliser le tableau créé à partir de irisGlobals.objects.all() pour obtenir les informations, nous utiliserons le tableau créé par cette fonction. Pour que tout cela fonctionne, nous devons assembler les deux formulaires.

defhandle_filters(request):
    globals = irisGlobal.objects.all()
    databaseFilter = request.GET.get("database")
    globalFilter = request.GET.get("global")
    sizeFilter = request.GET.get("size")
    allocFilter = request.GET.get("allocated")
<span class="hljs-keyword">if</span> databaseFilter:
    globals = globals.filter(Q(database__contains=databaseFilter))
<span class="hljs-keyword">else</span>:
    databaseFilter=<span class="hljs-string">""</span>
<span class="hljs-keyword">if</span> globalFilter:
    globals = globals.filter(Q(name__contains=globalFilter))
<span class="hljs-keyword">else</span>:
    globalFilter=<span class="hljs-string">""</span>
<span class="hljs-keyword">if</span> sizeFilter:
    globals = globals.filter(Q(size__gte=sizeFilter))
<span class="hljs-keyword">else</span>:
    sizeFilter=<span class="hljs-string">""</span>
<span class="hljs-keyword">if</span> allocFilter:
    globals = globals.filter(Q(allocatedsize__gte=allocFilter))
<span class="hljs-keyword">else</span>:
    allocFilter=<span class="hljs-string">""</span>
<span class="hljs-keyword">return</span> globals, databaseFilter, globalFilter, sizeFilter, allocFilter</code></pre>
defexport(request):
    globals = handle_filters(request)[0]
<formmethod="GET">
    {% csrf_token %}
    <inputtype="radio"name="exportLanguage"value="CSV"/><labelfor="CSV">CSV</label><inputtype="radio"name="exportLanguage"value="XML"/><labelfor="XML">XML</label><inputtype="radio"name="exportLanguage"value="JSON"/><labelfor="JSON">JSON</label><buttontype="submit"formaction="{% url 'export' %}"> Export </button><br/><inputtype="text"name="{{database}}"placeholder="Database"/><inputtype="text"name="{{global}}"placeholder="Global name"/><inputtype="number"name="{{size}}"placeholder="Size"step="0.001"/><inputtype="number"name="{{allocated}}"placeholder="Allocated size"step="0.001"/><buttontype="submit"formaction="{% url 'home' %}"> Filter </button></form>

 

Order table (with JavaScript)

This section will show you how I added order options to the table. There are many ways to do it, but today we will use JavaScript to learn something different from what we have done previously. Of course, it would be better if you had some knowledge of the language’s basics, but you will be able to follow up anyway. Nevertheless, the main focus is to understand the connections. They will be very similar to the CSS’s and will open even more doors for you to manage data from the portal.

Let’s begin! Open the index.html and add two <script> tags at the very bottom of the body. The first one should reference the dependencies. The second one will point to a .js file in our project (we will create it in the next step). Since we already added ‘{% load static %}’, this will be enough for the JavaScript file to start working.
 

<scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script><scriptsrc="{% static 'index.js'%}"></script>

 

Tableau de commandes (avec JavaScript)

Cette section vous montrera la manière dont j'ai ajouté des options de commande au tableau. Il existe de nombreuses façons de le faire, mais aujourd'hui nous allons utiliser JavaScript pour apprendre quelque chose de différent de ce que nous avons fait précédemment. Bien sûr, il serait préférable que vous ayez une certaine connaissance des bases du langage, mais vous pourrez suivre de toute façon. Néanmoins, l'objectif principal est de comprendre les connexions. Elles seront très similaires à celles du CSS et vous offriront encore plus de possibilités pour gérer les données du portail.

Commençons ! Ouvrez le fichier index.html et ajoutez deux balises tout en bas du corps. La première doit faire référence aux dépendances. La deuxième pointera vers un fichier .js dans notre projet (nous le créerons à l'étape suivante). Comme nous avons déjà ajouté "{% load static %}", cela suffira pour que le fichier JavaScript démarre.
 

<scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script><scriptsrc="{% static 'index.js'%}"></script>

 

Une fois la connexion établie, créez un fichier dans le dossier /static, et nommez-le index.js (ou quel que soit le nom que vous lui avez donné dans la référence). Si, à cette étape, vous ajoutez un console.log(“Hello world!”); au fichier et rechargez la page, vous verrez qu'il fonctionne déjà. Cela signifie que nous pouvons enfin commencer à créer la logique de commande.
     Nous voulons que le tableau soit dans l'ordre lorsque l'utilisateur clique sur un en-tête. Pour ce faire, il nous faut que l'affichage obéisse à l'utilisateur chaque fois qu'il clique sur l'un de ces en-têtes. Nous disposons du document à partir duquel nous sélectionnons chaque

élément du tableau et, pour chacun d'entre eux, nous voulons savoir quand il est cliqué.
 
  <pre class="codeblock-container" idlang="5" lang="JavaScript" tabsize="4"><code class="language-javascript hljs"><span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">"table th"</span>).forEach(<span class="hljs-function"><span class="hljs-params">header</span> =&gt;</span> {
header.addEventListener(<span class="hljs-string">"click"</span>, () =&gt; {
    
});

})

Nous pouvons maintenant lui communiquer ce qu'il doit faire une fois qu'il a reçu l'événement "click". Nous aurons besoin du tableau (le parent de , qui est le parent de , qui est le parent du "cliqué" ) et d'un nombre indiquant quel en-tête a été sélectionné. Nous devons également savoir si le tableau est déjà dans l'ordre croissant pour cette colonne. Nous pouvons alors appeler une fonction pour trier le tableau.

      <pre class="codeblock-container" idlang="5" lang="JavaScript" tabsize="4"><code class="language-javascript hljs"><span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">"table th"</span>).forEach(<span class="hljs-function"><span class="hljs-params">header</span> =&gt;</span> {
header.addEventListener(<span class="hljs-string">"click"</span>, () =&gt; {
    <span class="hljs-keyword">const</span> table = header.parentElement.parentElement.parentElement;
    <span class="hljs-keyword">const</span> columnIndex = <span class="hljs-built_in">Array</span>.prototype.indexOf.call(header.parentElement.children, header);
    <span class="hljs-keyword">const</span> isAscending = header.classList.contains(<span class="hljs-string">"ascending"</span>);
    sortTable(table, columnIndex+<span class="hljs-number">1</span>, !isAscending);
});

})

Il existe de nombreux algorithmes prédéfinis pour le tri des tableaux. Vous pouvez même télécharger et utiliser certains d'entre eux. Je veux éviter d'ajouter d'autres dépendances à ce projet. C'est pourquoi je m'en tiendrai à l'algorithme suivant.

document.querySelectorAll("table th").forEach(header => {
    header.addEventListener("click", () => {
        const table = header.parentElement.parentElement.parentElement;
        const columnIndex = Array.prototype.indexOf.call(header.parentElement.children, header);
        const isAscending = header.classList.contains("ascending");
        sortTable(table, columnIndex+1, !isAscending);
    });
})

There are many pre-built sorting table algorithms. You can even download and use some of them. I want to avoid adding more dependencies to this project. For that reason, I will stick to using the following algorithm.

functionsortTable(table, columnIndex, ascending = true) {
    const direction = ascending ? 1 : -1;                 // if its ascending, direction is 1, else is -1const header = $('th:nth-child('+columnIndex+')');    // gets the header with index columnIndexconst body = table.children[1]                        // gets the body (second children of table)const rows = Array.from(body.querySelectorAll("tr")); // creates an array for the rows in the body // removes previous order from every header
    table.querySelectorAll("th").forEach(th => th.classList.remove("ascending", "descending"));
<span class="hljs-comment">// adds the order to the clicked header</span>
header[<span class="hljs-number">0</span>].classList.toggle(<span class="hljs-string">"ascending"</span>, ascending);
header[<span class="hljs-number">0</span>].classList.toggle(<span class="hljs-string">"descending"</span>, !ascending);

<span class="hljs-comment">// algorithm for sorting the rows</span>
<span class="hljs-keyword">const</span> sorted = rows.sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> aColumn = a.querySelector(<span class="hljs-string">'td:nth-child('</span>+columnIndex+<span class="hljs-string">')'</span>).textContent;
    <span class="hljs-keyword">const</span> bColumn = b.querySelector(<span class="hljs-string">'td:nth-child('</span>+columnIndex+<span class="hljs-string">')'</span>).textContent;

    <span class="hljs-keyword">return</span> aColumn &gt; bColumn ? (<span class="hljs-number">1</span>*direction) : (<span class="hljs-number">-1</span>*direction);
});

<span class="hljs-comment">// removes the rows from the body and add the sorted rows</span>
<span class="hljs-keyword">while</span> (body.firstChild) {
	body.removeChild(body.firstChild);
}
body.append(...sorted);

}

La référence suivante explique l'algorithme susmentionné. https://dcode.domenade.com/tutorials/how-to-easily-sort-html-tables-with-css-and-javascript

Maintenant, avec un peu de CSS, vous avez un tableau parfaitement ordonné.


.ascending::after {
    content: "\25be";
}

.descending::after { content: "\25b4"; }

.ascending, .descending { background-color: #ffffff30; box-shadow: .1px .1px10.px #aaa; }

Une approche différente de l'ajout d'ordre

Une autre façon d'ajouter de l'ordre serait de créer un formulaire avec une option pour choisir chaque colonne (avec un peu de JavaScript, vous pouvez également utiliser les en-têtes pour la sélection), de le diriger vers un affichage qui utilise la fonction .order_by(“nom de la colonne”) sur l'objet globals, et de le renvoyer ordonné sur la fonction de rendu. Les deux options sont implémentées sur mon GitHub si vous voulez le vérifier. Faites attention au fait que dans la première méthode, nous ne changeons pas les données ou l'ensemble des résultats. Puisque nous ajustons seulement la façon dont elles sont affichées, cela n'affectera pas l'exportation.

Un autre conseil sur JavaScript

J'ai préparé pour vous une dernière chose qui pourrait être utile à connaître lorsque vous utilisez Django et JavaScript. Si vous voulez rediriger vers une URL (par exemple, la mise à jour), vous pouvez utiliser document.location.href = “update”; dans le fichier .js.

Si je suis un IRIS Dev, pourquoi voudrais-je savoir cela ?

Vous avez peut-être observé que l'ensemble de cet article n'utilise pas directement IRIS. Les tutoriels de la partie 2 seraient très similaires si nous utilisions n'importe quelle autre base de données. Cependant, si nous construisons tout ce qui est décrit ci-dessus en utilisant la technologie d'InterSystems comme base, nous aurons la possibilité d'adapter ce portail pour exécuter ou montrer beaucoup plus de choses en toute simplicité. Si vous êtes déjà un développeur IRIS, vous pouvez facilement imaginer comment ce simple portail peut combiner des informations provenant d'origines multiples (pas nécessairement globales), en plus de la capacité à fournir toutes sortes de traitements et d'analyses de données, avec les outils Cloud, IntegratedML (Machine Learning), Business Intelligence, Text Analytics et Natural Language Processing (NLP), etc. Lorsque tous les outils fournis par InterSystems offrent une interactivité complète, facile et magnifique avec les utilisateurs, à condition que toute la sécurité nécessaire soit en place, les possibilités dépassent l'imagination.

J'ai rassemblé ici quelques exemples pour illustrer mon point de vue. Vous pouvez avoir un capteur qui envoie des informations à l'API Callin d'InterSystems, intégré à un programme en C++. Ces données peuvent être interprétées, traitées et stockées dans IRIS, et leur analyse peut être affichée avec Django, ce qui permet à l'utilisateur de l'utiliser. Combiné avec InterSystems IRIS for Health et HealthShare, ce portail devient un outil de suivi des patients en thérapie. Il peut utiliser l'apprentissage automatique et la BI pour détecter et prédire les changements dangereux et envoyer des alertes avec SAM (System Alerting and Monitoring) au médecin responsable, qui peut être affiché sur le portail avec toutes les informations relatives au cas requises pour prendre une décision rapide.

Dans un autre contexte, mais avec une logique similaire, InterSystems IRIS for Supply Chain peut être utilisé avec ce portail pour permettre à l'utilisateur de voir ce qui pourrait compromettre le fonctionnement du système. Cette collaboration nous permet de prendre et de transmettre des décisions d'une manière qui rend l'ensemble de l'opération facilement compréhensible et contrôlable.
 

0
0 79
Article Lorenzo Scalese · Oct 4, 2023 8m read

Description du cas

Imaginons que vous soyez un développeur en Python ou que vous disposiez d'une équipe bien formée et spécialisée en Python, mais que le délai qui vous est imparti pour analyser certaines données dans IRIS soit serré. Bien sûr, InterSystems offre de nombreux outils pour toutes sortes d'analyses et de traitements. Cependant, dans le scénario donné, il est préférable de faire le travail en utilisant le bon vieux Pandas et de laisser IRIS pour une autre fois.
    Dans la situation décrite ci-dessus et dans bien d'autres cas, il se peut que vous souhaitiez extraire des tables d'IRIS pour gérer des données en dehors des produits d'InterSystems. Cependant, vous pouvez également avoir besoin de faire les choses dans l'autre sens lorsque vous avez un tableau externe dans n'importe quel format, à savoir CSV, TXT ou Pickle, que vous avez besoin d'importer et d'utiliser les outils d'IRIS sur celui-ci.
Indépendamment du fait que vous ayez ou non à faire face à un problème décrit ci-dessus, Innovatium m'a appris qu'il est toujours utile de connaître plusieurs façons de résoudre un problème de codage. La bonne nouvelle, c'est qu'il n'est pas nécessaire de passer par le processus laborieux de création d'un nouveau tableau, de transfert de toutes les lignes et d'ajustement de chaque type lorsque l'on importe un tableau d'IRIS.
    Cet article vous montrera comment transformer rapidement un tableau IRIS en une trame de données Pandas et inversement avec seulement quelques lignes de code. Vous pouvez consulter le code à partir de mon GitHub, où vous trouverez Jupiter Notebook avec toutes les étapes de ce tutoriel.

Importation du tableau d'IRIS

Bien entendu, vous devez commencer par importer les bibliothèques nécessaires à ce projet.

import pandas as pd
import sqlalchemy as db

L'étape suivante consiste à créer la connexion entre le fichier Python et l'instance IRIS. Pour ce faire, nous utiliserons la fonction create_engine() de SQLAlchemy, avec une chaîne en tant que paramètre. Cette chaîne doit contenir des informations sur le dialecte de l'opération, le nom d'utilisateur et le mot de passe, l'hôte et le port de l'instance, ainsi que l'espace de noms de destination. Pour plus d'informations sur les concepts de base de l'utilisation de sqlalchemy-iris, consultez l'un de mes articles précédents, SQLAlchemy - la façon la plus simple d'utiliser Python et SQL avec les bases de données d'IRIS.

engine = db.create_engine("iris://_system:SYS@localhost:1972/SAMPLE")
connection = engine.connect()

Ensuite, nous pouvons déclarer une variable qui contiendra la trame de données et appeler la fonction read_sql_table() de Pandas sur cette connexion, en spécifiant le nom du tableau sous la forme d'une chaîne avec le schéma. Vous pouvez également indiquer le schéma dans un autre argument, ce qui est, en fait, préférable car le fait d'avoir un point sur la chaîne de nom peut provoquer des erreurs dans certains cas.

df = pd.read_sql_table("NameAge", connection, schema="PD")

C'est une bonne pratique de vérifier si le tableau avec lequel nous travaillons existe dans le schéma que nous voulons utiliser et, bien sûr, s'il y a un schéma dont nous avons besoin au départ. Dans la dernière section de cet article, vous apprendrez comment le faire ainsi que quelques autres conseils. À partir de maintenant, si vous avez l'habitude d'utiliser Pandas, vous pouvez effectuer toutes les modifications et analyses que vous souhaitez puisque vous savez quoi faire. Explorez l'exemple suivant pour voir comment cela fonctionne.

Envoi d'un tableau à IRIS

Avant de commencer, modifions un peu notre trame de données, à titre d'exemple. Nous pouvons adapter les valeurs d'une colonne à nos besoins (par exemple, ajouter des lignes et des colonnes, etc.). Après avoir joué un peu, j'ai mis les noms en minuscules et j'ai ajouté une nouvelle personne et une colonne sur la base des données existantes. Vous pouvez consulter l'illustration suivante pour voir le résultat.

Nous pouvons maintenant le renvoyer à IRIS par une seule ligne de code. Il suffit de spécifier le moteur et le nom du tableau.

df.to_sql("NameAge", con=engine, schema="PD", if_exists="replace")

Une fois de plus, nous devons placer le schéma dans un argument séparément du nom du tableau afin d'éviter certaines erreurs et un comportement indésirable. En outre, l'argument if_exists spécifie ce qu'il faut faire s'il existe déjà un tableau portant le même nom dans le schéma concerné. Les valeurs possibles sont : "replace" (remplacer), "fail" (échouer, la valeur par défaut) et "append" (ajouter). Bien entendu, l'option "replace" supprime le tableau et en crée un nouveau à l'aide d'une commande SQL, tandis que l'option "append" ajoute les données au tableau existant. N'oubliez pas que cette méthode ne vérifie pas les valeurs répétées ; soyez donc prudent lorsque vous utilisez cet attribut. Enfin, la valeur "fail" provoque l'erreur suivante :

Gardez à l'esprit que si vous spécifiez un nom de tableau qui n'existe pas, la fonction le créera.

Vous pouvez maintenant lancer une requête dans IRIS pour voir ce qu'il y a de nouveau ou aller dans le Portail de gestion dans la partie consacrée à SQL. N'oubliez pas que si vous avez utilisé la valeur "replace", vous devez consulter le code source de la classe, car la méthode la réécrit complètement. Cela signifie que si vous avez implémenté des méthodes, vous devez les laisser dans une superclasse.

Plus d'astuces sur sqlalchemy-iris

Si vous rencontrez quelques problèmes que vous n'avez pas pu résoudre avec les informations partagées dans d'autres communautés ou forums liés au code de votre application, vous trouverez peut-être ici l'aide dont vous avez besoin. Vous y découvrirez une liste d'astuces pour trouver des détails sur le moteur et le dialecte.

Caractéristiques spécifiques au dialecte

SQL Alchemy travaille avec des dialectes qui sont automatiquement choisis en fonction de votre moteur. Lorsque vous utilisez la fonction create_engine() pour vous connecter à une base de données IRIS, le dialecte choisi est sqlalchemy-iris by Dmitry Maslennikov.
Vous pouvez accéder à ses caractéristiques et les modifier à l'aide de la propriété de dialecte de votre moteur.

engine = db.create_engine("iris://_system:SYS@localhost:1972/SAMPLE")
engine.dialect

Avec l'extension IntelliCode de VSCode, vous pouvez rechercher toutes les options de cette propriété ou consulter le code source sur CaretDev's GitHub.

Vérification des schémas disponibles dans un moteur

Une fonction spéciale du dialecte digne d'être soulignée est la fonction get_schema_names(). Faites attention ! Les informations suivantes peuvent être cruciales pour vous si vous voulez éviter les erreurs dans votre code et pour l'itération.

connection = engine.connect()
engine.dialect.get_schema_names(connection)

 

Vérification des tableaux disponibles dans un schéma

Examinons une situation semblable. Il se peut que vous ayez besoin de connaître les tableaux disponibles à partir d'un schéma. Dans ce cas, vous pouvez utiliser l'inspection. Exécutez la fonction inspect() sur le moteur et enregistrez-la dans une variable. Cette même variable sera utilisée pour accéder à une autre fonction, get_table_names(). Elle renverra une liste des noms de tableaux dans le schéma spécifié ou dans la valeur par défaut "SQLUser".

inspection = db.inspect(engine)
inspection.get_table_names(schema="Sample")

De plus, si vous souhaitez utiliser plus de fonctionnalités de SQL Alchemy sur vos données, vous pouvez déclarer une base et faire en sorte que ses métadonnées reflètent un schéma du moteur.

b = db.orm.declarative_base()
b.metadata.reflect(engine, schema="Sample")

Si vous avez besoin de plus d'informations pour résoudre ce problème, consultez la Documentation SQL Alchemy et le dépôt sqlalchemy-iris GitHub Repository. Vous pouvez également m'envoyer un message ou laisser un commentaire, et nous essaierons de découvrir le secret ensemble.

Considérations finales

L'approche de mise en œuvre présentée dans cet article met l'accent sur l'utilisation des instances IRIS en tant que fournisseurs de services en nuage et permet d'effectuer une analyse sur différentes bases. Elle facilite la surveillance simultanée de toutes les instances dans toutes leurs qualités et la comparaison de leurs performances et de leur utilisation. Si vous combinez ces connaissances avec le développement décrit dans un autre article à propos d'un portail réalisé avec Django, vous pouvez rapidement construire un gestionnaire puissant pour autant de fonctionnalités et d'instances que vous le souhaitez.
    Cette implémentation est également un moyen efficace de transférer des données de l'extérieur d'IRIS vers une classe bien construite. Comme vous êtes peut-être familier avec d'autres fonctions trouvées dans Pandas pour traiter de nombreux langages différents, à savoir CSV, JSON, HTML, Excel et Pickle, il vous sera facile de changer read_sql\table en read_csv, read_json, ou toute autre option. Certes, je dois vous avertir que l'intégration avec InterSystems à partir de certains types de données n'est pas une fonctionnalité intégrée et qu'elle n'est donc pas toujours facile à mettre en œuvre. Cependant, l'union de SQL Alchemy et de Pandas sera toujours utile pour exporter des données depuis IRIS.
    Ainsi, dans cet article, nous avons appris qu'IRIS possède tous les outils dont vous avez besoin pour vous aider à développer et à intégrer facilement les dispositifs existants de votre système ou les dispositifs de votre domaine d'expertise.
 

0
0 146
Article Lorenzo Scalese · Juil 31, 2023 11m read

Notre objectif

Dans le dernier article, nous avons parlé de quelques éléments de démarrage pour Django. Nous avons appris à commencer le projet, à nous assurer que nous disposons de tous les éléments requis et à créer une matrice CRUD. Cependant, aujourd'hui, nous allons un peu plus loin.
Aujourd'hui, nous allons donc connecter IRIS à un environnement Python, construire quelques fonctions et les afficher sur une page web. Ce sera similaire à la dernière discussion, mais nous irons assez loin pour que vous puissiez faire quelque chose de nouveau, mais pas assez pour que vous vous sentiez perdus.
Dans ce projet, nous obtiendrons des informations sur les globales dans IRIS afin de suivre leur taille et de comprendre le coût de chaque espace de noms et de chaque table en Mo.

Le projet

Chaque étape est disponible sur mon Référentiel GitHub, dans l'historique des commits.

Les éléments de démarrage

Nous suivons quelques étapes similaires à celles de l'article précédent, alors cette partie vous sera familière.

  • Allez dans le répertoire souhaité sur le terminal et tapez ce qui suit.

django-admin startproject globalSize

  • Ajoutez requirements.txt avec le texte suivant, et tapez

pip install -r requirements.txt

pour être sûr de remplir les conditions requises.

# Django en lui-même
django>=4.2.1# Pilote InterSystems IRIS pour Django
django-iris==0.2.2
  • Dans globalSize/settings.py, ajoutez IRIS dans les configurations DATABASES :
DATABASES = {
	‘default’: {
		‘ENGINE’: ‘django_iris’,
		‘NAME’: ‘USER’,
		‘USER’: ‘_system’,
		‘PASSWORD’: ‘SYS’,
		‘HOST’: ‘localhost’,
		‘PORT’: 1972,
	}
}
  • N'oubliez pas d'ajouter un .gitignore et un .editorconfig. Il est également pratique d'avoir un linter de votre choix, mais cela dépasse le cadre de cet article.

 

Création de l'application et du modèle

Nous avons créé une application et un modèle dans le dernier article, cette section devrait donc vous être familière, même s'il s'agit d'une application et d'un modèle différents.

  • Pour créer l'application, saisissez

python manage.py startapp globals

  • Dans _globals/models.py, _créez un modèle avec les informations que vous souhaitez afficher sur vos globales :
classirisGlobal(models.Model):
	database = models.CharField(max_length=40)
	name = models.CharField(max_length=40)
	allocatedsize = models.FloatField()
	size = models.FloatField()


	def__str__(self):return self.name
  • Dans _settings.py, _ajoutez la nouvelle application à INSTALLED_APPS :
INSTALLED_APPS = [
	…,
	‘globals’,
]

 

Configuration des URL et de la page d'accueil

Une fois de plus, nous suivons quelques étapes similaires à celles de l'article précédent.

  • La fonction incluse dans django.urls est importée dans _globalSize/urls.py et un nouveau chemin est ajouté à globals.urls dans urlpatterns.
from django.urls import path, include
urlpatterns = [
    …,
    path(‘globals/’, include(‘globals.urls’)),
]
  • Créez les URLs pour l'application, en ajoutant le fichier globals/urls.py avec le texte suivant.
from django.urls import path
from .views import home
urlpatterns = [
	path(‘’, home),
]
  • Créez la vue importée dans la dernière étape. Dans view.py, ajoutez la fonction ci-dessous.
defhome(request):return render(request, “index.html”)
  • Pour finir, ajoutez le fichier globals/templates/index.html et générez la page d'accueil comme vous le souhaitez. Consultez l'exemple ci-dessous :


  
    hello world!
  

Si vous entrez les commandes ci-dessous et suivez le lien http://127.0.0.1:8000/globals/, vous aurez une page affichant "hello world !".

python manage.py makemigrations<br>python manage.py migrate<br>python manage.py runserver

Affichage des globales dans les pages d'accueil et d'administration

  • Importez le modèle et enregistrez-le dans le fichier admin.py.
from .models import irisGlobal
admin.site.register(irisGlobal)
  • Importez le modèle dans views.py et renvoyez-le dans la fonction.
from .models import irisGlobal
defhome(request):
	globals = irisGlobal.objects.all()
	return render(request, “index.html”, {“globals”: globals})
  • Nous pouvons maintenant accéder aux valeurs globales à partir de _index.html _comme nous le souhaitons. Consultez l'exemple ci-dessous.
ID  -  DATABASE          /       GLOBAL       - Size / Allocated
  {% for global in globals %}
  >
    {{ global.id }} - {{ global.database }}    {{ global.name  }}  -  {{ global.size  }}  /  {{ global.allocatedsize }}
  
  {% endfor %}

 

Récupération de données

Récupération de données
À ce stade, le projet est prêt à recevoir des informations. Il existe de nombreuses façons de modeler ce projet, mais pour ma part, j'utiliserai l'approche de Python afin que nous puissions apprendre une nouvelle solution qu'il est possible d'intégrer à Django et à IRIS.
Nous avons besoin de quelques méthodes pour récupérer toutes les données. Nous pouvons utiliser InterSystems IRIS Cloud SQL avec le pilote DB-API pour nous connecter à l'instance que nous voulons analyser - il n'est pas nécessaire que ce soit la même que celle où nous avons connecté Django.
L'organiser dans un nouveau dossier que nous pouvons traiter comme un module est une bonne pratique. Pour cela, créez le dossier api dans les globaux, ajoutez un fichier vide _init_.py_ pour que Python le reconnaisse comme un module, et commencez à écrire le fichier qui contiendra les méthodes. Nous pouvons l'appeler methods.py.


Création de la connexion

Pour connecter notre environnement Python à l'IRIS d'InterSystems, nous devons suivre quelques étapes décrites dans la section "ObjectScript dans l'environnement Python" de l'article précédent [Python et IRIS dans la pratique] (https://community.intersystems.com/post/python-and-iris-practice-examples).
A partir de maintenant, c'est simple ; nous importons iris, nous passons l'adresse de la connexion (l'instance d'IRIS que nous voulons analyser dans le format suivant : host:port/namespace), un nom d'utilisateur et un mot de passe à la méthode iris.connect et nous créons IRIS de Python. Jetez un coup d'œil au code ci-dessous.

import intersystems_iris as iris
from django.db import connection as djangoconnection

# connection par iris
conn_params = djangoconnection.get_connection_params()
conn_params[“namespace”] = “%SYS”
connection = iris.connect(**conn_params)
irisPy = iris.createIRIS(connection)

 

Obtention des répertoires de bases de données

Puisque nous voulons récupérer les tailles des globales, nous avons besoin (bien sûr) de leurs tailles, de leurs noms et de leurs adresses - ce que nous appelons des bases de données.
Je vais vous montrer une version simplifiée de la fonction, mais rappelez-vous qu'il est préférable de vérifier chaque étape et chaque connexion, et de lancer une exception si quelque chose ne va pas.
Tout comme nous le ferions en ObjectScript, nous avons besoin d'une requête SQL pour pouvoir la préparer, l'exécuter et récupérer une liste contenant tous les répertoires de la base de données dans son jeu de résultats. Nous pouvons faire tout cela facilement avec les fonctions "irisPy.classMethodSomething()", où Something (quelque chose) représente le type que la méthode doit retourner, et irisObject.invoke(), où nous pouvons accéder à n'importe quoi à partir de l'irisObject référencé. Voici un exemple.

defgetAllDatabaseDirectories():try:
	   # vérification de la connexion faite dans irisPy, et si elle est fixée à l'espace de noms %SYS
	   databaseDirectoriesList = []
	   with connection.cursor() as cursor:
		cursor.execute(“SELECT DISTINCT %EXACT(Directory) FROM Config.Databases WHERE SectionHeader = ?”, [“Databases”,],)
		databaseDirectoriesList = [row[0] for row in cursor]

    except Exception as error:
        return str(error)

    return databaseDirectoriesList

La variable statement est définie comme un objet généré par la méthode %New de la classe IRIS %SQL.Statement. Il est alors possible d'invoquer la méthode %Prepare à partir de l'objet instancié, avec une chaîne de requête comme argument. Ensuite, nous pouvons invoquer les méthodes %Execute et %Next pour exécuter la requête et boucler sur l'ensemble des résultats, en ajoutant les informations souhaitées à une liste Python pour un accès facile.
Il est facile de trouver chaque répertoire de base de données dans le tableau Config.Databases, situé uniquement dans l'espace de noms %SYS de chaque instance d'IRIS. Consultez-la dans le portail de gestion si vous le souhaitez, vous y trouverez d'autres informations intéressantes.

Récupération de toutes les globales d'une base de données

Cette fonction est très similaire à la précédente. Cependant, nous disposons maintenant d'une requête de classe prête à l'emploi. Une fois de plus, nous avons besoin d'une requête SQL, nous pouvons donc préparer la requête DirectoryList à partir de la classe %SYS.GlobalQuery. Ensuite, nous l'exécutons avec un répertoire de base de données comme argument et nous récupérons une liste contenant tous les globales de cette base de données.

defgetGlobalsList(databaseDirectory: str):try:
        statement = irisPy.classMethodObject("%SQL.Statement", "%New")
        status = statement.invoke("%PrepareClassQuery", "%SYS.GlobalQuery","DirectoryList")

        result = statement.invoke("%Execute", databaseDirectory)

        globalList = []
        while (result.invoke("%Next")!=0):
            globalList.append(result.invoke("%Get", "Name"))

    except Exception as error:
        return str(error)

    return globalList

Récupération des tailles des globales et des tailles allouées

Enfin, nous pouvons accéder aux informations cibles. Heureusement, IRIS dispose d'une méthode intégrée pour récupérer la taille et la taille allouée à condition de fournir une base de données et une paire de globales.

defgetGlobalSize(databaseDirectory: str, globalName: str):try:
        globalUsed = iris.IRISReference(0)
        globalAllocated = iris.IRISReference(0)
        status = irisPy.classMethodObject("%GlobalEdit", "GetGlobalSize", databaseDirectory, globalName, globalAllocated, globalUsed, 0)

    except Exception as error:
        return str(error)

    return (globalUsed.getValue(), globalAllocated.getValue())

Cette fois, nous avons besoin de la fonction IRISReference(0) du module iris pour recevoir les tailles de la fonction "GetGlobalSize" par référence. Ensuite, nous pouvons accéder à la valeur avec la méthode getValue().

Affichage complet sur la page d'accueil

Enfin, nous pouvons utiliser ces fonctions pour afficher les données sur la page d'accueil. Nous disposons déjà d'un moyen pour accéder aux informations et d'un tableau, il ne nous reste plus qu'à le remplir. Pour cela, je veux créer un bouton de mise à jour.
Tout d'abord, nous ajoutons un lien vers le fichier index.html.

href = "{% url 'update' %}">updatea>body>

Ajoutez le lien vers la liste urlpatterns, dans urls.py.

Add the link to the urlpatterns list, in urls.py.
from .views import home, update
urlpatterns = [
    path('', home),
    path('update', update, name="update"),
]

Ensuite, créez la vue, dans views.py.

from django.shortcuts import render, redirect
from .api.methods import *
defupdate(request):
    irisGlobal.objects.all().delete()
    databaseList = getAllDatabaseDirectories()

    for database in databaseList:
        globalList = getGlobalsList(database)

        for glob in globalList:
            used, allocated = getGlobalSize(database, glob)
            irisGlobal.objects.create(database=database, name=glob, size=used, allocatedsize=allocated)

    return redirect(home)

 

Pour cette vue, nous devons d'abord importer la fonction de redirection de django.shortcuts, ainsi que les méthodes que nous venons de construire.  
C'est une bonne idée de supprimer toutes les données précédentes du tableau afin que les globales éventuellement supprimées disparaissent. Comme le nombre de globales n'est probablement pas gigantesque, il est préférable de procéder ainsi plutôt que de vérifier chaque enregistrement pour voir s'il a été supprimé ou s'il a besoin d'une mise à jour.  
Ensuite, nous obtenons tous les répertoires des bases de données afin de pouvoir, pour chaque base de données, vérifier tous les globales qui s'y trouvent, et pour chaque globale, nous pouvons avoir sa taille utilisée et sa taille allouée.  
À ce stade, le modèle Django est rempli et prêt à récupérer des données, nous redirigeons donc vers la vue d'accueil.  
Si vous accédez à <http://127.0.0.1:8000/globals/> et cliquez sur le lien de mise à jour que nous avons ajouté, la page devrait se recharger et dans quelques secondes, elle affichera la liste des globales, avec ses bases de données, ses tailles et ses tailles allouées, comme dans l'image ci-dessous.


Ajout d'une agrégation

Vous seriez surpris de savoir à quel point il est simple d'ajouter quelques options d'analyse rapide, telles qu'une somme ou un décompte. Il n'est pas nécessaire de maîtriser Django pour créer quelques tableaux de bord sur cette page et, après cette section, vous devriez être en bonne position pour commencer.
Nous savons déjà que la vue accueil est responsable du rendu de l'index. Jusqu'à présent, nous avons généré la variable "globals", contenant toutes les données, et l'avons passée à l'index.html. Nous allons faire quelque chose de similaire, mais avec des fonctions d'agrégation. Nous allons créer une variable pour chaque somme, utiliser les méthodes aggregate() et Sum(), et les ajouter à l'argument "context list" de la fonction "render". Et bien sûr, n'oubliez pas d'importer "Sum" de django.db.models. Consultez la fonction ci-dessous.

defhome(request):
	globals = irisGlobal.objects.all()
	sumSize = globals.aggregate(Sum("size"))
	sumAllocated = globals.aggregate(Sum("allocatedsize"))
	return render(request, "index.html", {"globals": globals, "sumSize": sumSize, "sumAllocated":sumAllocated})

Nous pouvons maintenant l'ajouter au fichier index.html et ajouter quelques paragraphes sous la liste (

élément). Dans ces paragraphes, nous pouvons accéder au décompte de toutes les globales et aux sommes, comme indiqué ci-dessous.

…
  showing results for {{globals.count}} globalsp>total size: {{sumSize.size__sum}}p><total allocated size: {{sumAllocated.allocatedsize__sum}}p>body>html>

Actualisez le lien et vous devriez obtenir le résultat suivant.


La fin... ou presque

Dans cet article, nous avons appris comment InterSystems IRIS stocke les données en mémoire, comment y accéder à partir de Python, comment construire une API, et comment utiliser IRIS comme un système en nuage, de sorte que nous puissions le suivre et l'analyser facilement. Nous pouvons voir à l'horizon des requêtes plus complexes, la création de tableaux de bord, l'automatisation des mises à jour et l'ajout d'un système de notification.
Dans le prochain article, je me rapprocherai de cet horizon, en montrant comment filtrer et ordonner les données avant de les afficher, en ajoutant quelques options modifiables côté client, et enfin, en ajoutant une pincée de CSS pour donner du charme à l'ensemble.
Souhaitez-vous voir quelque chose que je n'ai pas encore dit ? N'hésitez pas à me contacter si vous avez des idées ou des besoins sur lesquels vous aimeriez que j'écrive.

0
0 171
Article Sylvain Guilbaud · Juil 10, 2023 13m read

Découvrir Django

Django est un framework web conçu pour développer des serveurs et des API, et pour traiter des bases de données de manière rapide, évolutive et sécurisée. Pour ce faire, Django fournit des outils permettant non seulement de créer le squelette du code, mais aussi de le mettre à jour sans souci. Il permet aux développeurs de voir les changements presque en direct, de corriger les erreurs avec l'outil de débogage et de traiter la sécurité avec facilité.

Pour comprendre le fonctionnement de Django, examinons l'image :

0
0 164
Article Guillaume Rongier · Juin 26, 2023 3m read

Introduction

Dans certains des derniers articles, j'ai parlé des types entre IRIS et Python, et il est clair qu'il n'est pas facile d'accéder aux objets d'un côté à l'autre.  

Heureusement, un travail a déjà été fait pour créer SQLAlchemy-iris (suivez le lien pour le voir sur Open Exchange), qui rend tout beaucoup plus facile pour Python d'accéder aux objets d'IRIS, et je vais montrer les starters pour cela.

Merci à @Dmitry Maslennikov !

Installation

Pour l'installer, il suffit d'ouvrir votre terminal avec un accès administrateur et de taper

pip install sqlalchemy-iris

et les pré-requis seront également installés pour vous, si nécessaire.

Utilisation

Maintenant, sur un fichier python, vous pouvez importer le module, vous connecter à la base de données et jouer avec sqlalchemy comme vous le souhaitez. Si vous vous sentez à l'aise, vous pouvez suivre les étapes suivantes :

  • Importez "create_engine" de sqlalchemy et créez le moteur avec la chaîne "iris://username:password@IP:port/namespace". Bien sûr, vous pouvez importer tout le module, mais "create_engine" crée une instance de Engine (sqlalchemy.engine, pour plus d'informations cliquez ici) qui a toutes les sous-classes nécessaires pour ce que je présente ici..
from sqlalchemy import create_engine

engine = create_engine("iris://_SYSTEM:SYS@localhost:1972/SAMPLE")
  • Créez la connexion (sqlalchemy.engine.connection, pour plus d'informations cliquez ici) ainsi vous pourrez travailler avec des transactions, des exécutions simples, etc.
conn = engine.connect()

C'est très bien ! Vous avez maintenant configuré l'accès à votre base de données.

Pour une simple requête SELECT, et pour itérer à travers l'ensemble des résultats, faites ce qui suit (comme d'habitude, en utilisant comme exemple le tableau créé dans cet article):

query = 'SELECT Name, Age  from Sample.PersistentData WHERE Age >=21'
result = conn.exec_driver_sql(query)

Maintenant, le résultat est un CursorResult (sqlalchemy.engine.CursorResult). Vous pouvez vérifier tout ce que vous pouvez faire avec un CursorResult ici, dans la documentation officielle. C'est tout ce que vous avez à faire pour une simple itération :

print("Name, Age")
for row in result:
    print(row[0], ", ", row[1])

 et avec un peu de formatage, vous pouvez avoir un résultat du type suivant :

Maintenant comme vous savez par où commencer, vous pouvez également consulter la documentation officielle pour plus de détails et de possibilités.

PS : vous pouvez également importer "text" de sqlalchemy et exécuter la requête comme suit

result = conn.execute(text(query))

ce qui produira exactement les mêmes résultats.

Conclusion

Vous pouvez également exécuter des DDL et toute autre instruction DML, et il y a un support encore plus important pour travailler avec un un mapping objet-relationnel ORM (_Object Relational Mapping), _mais comme ceci n'est qu'un "démarrage", je n'irai pas plus loin.

Souhaitez-vous plus de tutoriels avec SQLAlchemy ? Peut-être la prochaine fois en utilisant ORM ?
N'hésitez pas à me contacter si vous avez des doutes !

0
1 618