Modèles plans et vues pour les pays
=============
.. container:: section
:name: blueprints-and-views
.. rubric:: Modèles, plans et vues
:name: sql-plans-et-vues
Créez le package nommé ``models`` (un dossier) dans le dossier ``mobility``.
Ajoutez dans ce nouveau package un fichier nommé ``country.py`` et ajoutez-y le code suivant :
.. container:: code-block-caption
``mobility/models/country.py``
.. code-block:: python
from mobility.db import get_db
def get_country_list():
db = get_db()
return db.execute('SELECT * FROM country ORDER BY iso_country').fetchall()
def search_by_iso(iso_country: int):
db = get_db()
return db.execute('SELECT * FROM country WHERE iso_country=?', (iso_country,)).fetchall()
.. warning::
Il est **TRÈS** important d'utiliser le mécanisme de remplacement avec des ``?``.
En effet, si vous faites
``db.execute("SELECT * FROM country WHERE iso_country=" + iso_country)``
au lieu de
``db.execute("SELECT * FROM country WHERE iso_country=?", (iso_country,) )``
ce qui est syntaxiquement correct, l'utilisateur peut envoyer un ``iso_country`` tel que ``iso_country OR 1=1``, ce qui
introduit une tautologie et permet d'extraire... l'entièreté de la base de données !
En fonction de l'utilisation du résultat, cela peut être extrêmement grave.
La RGPD prévoit des amendes jusqu'à 20 millions d'euros pour ce genre de manque de prévoyance élémentaire.
La fonction ``get_country_list`` retourne une liste de tous les pays dans la base de données, ordonnée par code iso.
La fonction ``search_by_iso`` retourne le code iso, ainsi que le nom du pays dont le code iso est passé en paramètre.
Ajoutez cette classe en dessous des fonctions créées précédemment.
.. container:: literal-block-wrapper docutils
:name: id3
.. container:: code-block-caption
``mobility/models/country.py``
.. code-block:: python
class Country:
def __init__(self, name, iso_country):
self.name = name
self.iso_country = iso_country
def delete(self):
db = get_db()
db.execute("DELETE FROM country WHERE iso_country=?", (self.iso_country,))
db.commit()
def save(self):
db = get_db()
db.execute("INSERT INTO country(iso_country,name) VALUES(?, ?)",
(self.iso_country, self.name))
db.commit()
@staticmethod
def get(iso_country: int):
db = get_db()
data = db.execute(
'SELECT * FROM country WHERE iso_country=?', (iso_country,)).fetchone()
if data is None:
return None
else:
return Country(data["name"], data["iso_country"])
La classe ``Country`` est une classe qui représente un pays. Elle a deux attributs : ``name`` et ``iso_country``.
Elle a aussi trois méthodes : ``delete``, ``save`` et ``get``. La méthode ``delete`` supprime le pays de la base de données, la méthode ``save`` ajoute le pays à la base de données et la méthode ``get`` retourne une instance de ``Country`` à partir d'un code iso.
Nous allons créer une nouvelle page ``mobility/templates/country.html`` qui va afficher la liste de tous les pays, en appelant la fonction ``get_country_list``. N'oubliez pas d'utiliser le principe d'héritage et d'étendre ``base.html``. Le fichier ``country.html`` vous est déjà donné ci-dessous.
.. container:: literal-block-wrapper docutils
:name: id3
.. container:: code-block-caption
``mobility/country.py``
.. code-block:: python
from flask import (
Blueprint, render_template
)
from mobility.models.country import get_country_list
bp = Blueprint('country', __name__)
# route code
@bp.route('/country')
def country_list():
countries = get_country_list()
return render_template("country.html", countries=countries)
.. container:: literal-block-wrapper docutils
:name: id3
.. container:: code-block-caption
``mobility/templates/country.html``
.. raw:: html
Solution (Essayez d'abord par vous-même)
.. code-block:: html+jinja
{% extends "base.html" %}
{% block content %}
Country list
Name
ISO Code
{% for country in countries %}
{{ country['name'] }}
{{ country['iso_country'] }}
{% endfor %}
{% endblock %}
.. raw:: html
---------------------
Interagir avec la base de données depuis le site web
---------------------
Pour le moment, notre base de données est presque vide, les fonctions que nous avons écrites ne sont donc pas encore très utiles. Nous aimerions avoir la possibilité d'ajouter ou supprimer un pays dans la base de données depuis notre site web. Pour cela, nous allons ajouter deux nouvelles routes à notre plan.
.. container:: literal-block-wrapper docutils
:name: id3
.. container:: code-block-caption
``mobility/country.py``
.. code-block:: python
@bp.route("/create_country", methods=["POST"])
def country_create():
iso_country = request.form["iso_country"]
if not search_by_iso(str(iso_country)):
print("Creating country")
name = request.form["name"]
country = Country(name, iso_country)
country.save()
print("Country already exists")
return redirect(url_for("country.country_list"))
@bp.route("/delete_country/")
def country_delete(iso_country):
country = Country.get(iso_country)
if country:
country.delete()
return redirect(url_for("country.country_list"))
Vous constatez dans votre IDE que les imports ``request`` , ``redirect``, ``url_for`` sont manquants. Ajoutez-les en haut de votre fichier.
Vous devez également importer la classe ``Country`` et les fonctions ``search_by_iso_code`` et ``get_country_list`` depuis ``mobility/models/country.py``. Votre début de fichier devrait ressembler à ceci :
.. container:: literal-block-wrapper docutils
:name: id3
.. container:: code-block-caption
``mobility/country.py``
.. code-block:: python
from flask import (
Blueprint, render_template, request, redirect, url_for
)
from mobility.models.country import get_country_list, search_by_iso, Country
bp = Blueprint('country', __name__)
Il reste à mettre à jour le template pour ajouter un formulaire pour ajouter une ville et un bouton pour supprimer une ville. Retournez dans votre fichier *country.html* et ajoutez le code suivant.
.. container:: literal-block-wrapper docutils
:name: id3
.. container:: code-block-caption
``mobility/template/country.html``
.. code-block:: html+jinja
{% extends "base.html" %}
{% block content %}
{% endblock %}
Nous avons créé un formulaire pour ajouter un pays ainsi qu'un bouton pour en supprimer un. Le formulaire envoie une requête POST à la route ``country_create`` et le bouton envoie une requête GET à la route ``country_delete``.
N'oubliez pas d'ajouter la nouvelle route :
.. container:: literal-block-wrapper docutils
:name: id3
.. container:: code-block-caption
``mobility/__init__.py``
.. code-block:: python
...
from . import country
app.register_blueprint(country.bp)
...
Testez votre application en lançant le serveur avec la commande ``flask --app=mobility --debug run``
Dans votre navigateur, allez sur la page ``http://127.0.0.1:5000/country``.
Ensuite, ajoutez des pays à votre base de données en utilisant le formulaire que vous venez de créer. Vous pouvez aussi supprimer des pays en cliquant sur le bouton "Delete" à côté de chaque pays.
Vous devriez avoir une page qui ressemble à ceci :
.. image:: ../_images/country.png
:class: screenshot align-center
.. only:: html
Continuez en lisant le document pour ajouter les aéroports `Modèles plans,et vues pour les aéroports `__.