Développement Web avec Django : Gestion de Contenu et API

Notes de Développement Django - Semaine 25

Blog et Gestion de Contenu

Dans le cadre du développement d'un système de blog, nous avons exploré plusieurs fonctionnalités essentielles :

  • Éditeur de contenu KindEditor : Intégration d'un éditeur WYSIWYG pour une expérience utilisateur améliorée
  • Filtrage de contenu avec BeautifulSoup4 : Nettoyage et validation du contenu HTML avant sauvegarde
  • Modèle Singleton : Implémentation d'un pattern singleton pour la gestion de configurations globales
  • Opérations transactionnelles : Utilisation de from django.db import transaction pour garantir l'intégrité des données
  • Filtrage avancé : Utilisation des fonctions natives de la base de données pour optimiser les requêtes complexes

Pour installer BeautifulSoup4, utilisez la commande :

pip3 install beautifulsoup4

Système de Gestion de Configuration (CMDB)

Un CMDB (Configuraton Management Database) permet de centraliser et gérer les informations sur les composants d'un système informatique. Plusieurs approches peuvent être envisagées pour la collecte automatique des données :

Méthodes de collecte d'informations système

  1. Approche sans agent (Paramiko, Ansible, Fabric) :
    • Avantages : Aucune dépendance requise sur les machines distantes
    • Inconvénients : Performance réduite due aux connexions SSH individuelles
    • Implémentation : Récupération des hôtes via API, connexion SSH pour collecter les données, puis parsing en format structuré
  2. SaltStack :
    • Avantages : Architecture centralisée avec meilleure performance que Paramiko
    • Inconvénients : Configuraton plus complexe
    • Fonctionnement : Utilisation du Salt API pour collecter les données des minions
  3. Puppet :
    • Architecture maître-esclave avec certificats uniques (certname)
    • Synchronisation toutes les 30 minutes par défaut
    • Personnalisation des rapports (reports) via des scripts Ruby
    • Facteurs personnalisés pour l'inventaire matériel
  4. Agent léger :
    • Avantages : Performance optimale grâce à l'exécution locale
    • Inconvénients : Déploiement requis sur chaque machine
    • Exemple de collecte : v = subprocess.getoutput('hostname')

Exemple de collecte d'informations système

# Script Python pour collecter des informations réseau et disque
import subprocess

def collect_system_info():
    # Récupération du nom d'hôte
    hostname = subprocess.getoutput('hostname')
    
    # Collecte des informations réseau
    network_info = subprocess.getoutput('ip addr show')
    
    # Collecte des informations disque
    disk_info = subprocess.getoutput('df -h')
    
    return {
        'hostname': hostname,
        'network': network_info,
        'disk': disk_info
    }

Gestion des requêtes inter-domaines (JSONP)

Le JSONP (JSON with Padding) est une technique permettant de contourner les restrictions de sécurité du navigateur (politique de même origine) pour effectuer des requêtes vers d'autres domaines.

Principe de fonctionnement

  • Les navigateurs bloquent les requêtes AJAX entre domaines différents
  • Les balises <script> ne sont pas soumises à cette restriction
  • Le serveur doit renvoyer du code JavaScript exécutable
  • Seulement compatible avec les requêtes GET

Implémentation côté client

// Création dynamique d'une balise script
function makeJsonpRequest(url, callbackName) {
    const script = document.createElement('script');
    script.src = `${url}?callback=${callbackName}`;
    document.head.appendChild(script);
    
    // Définition de la fonction de callback
    window[callbackName] = function(data) {
        console.log('Données reçues :', data);
        document.head.removeChild(script);
    };
}

// Exemple d'utilisation
makeJsonpRequest('http://api.example.com/data', 'handleResponse');

Alternative moderne : CORS

CORS (Cross-Origin Resource Sharing) est une solution plus sécurisée et flexible que JSONP, supportant tous les types de requêtes HTTP.

Filtres Django pour la gestion de contenu

Voici des exemples de filtres personnalisés pour gérer l'affichage de contenu filtré dans une application Django :

from django import template
from django.utils.safestring import mark_safe

register = template.Library()

@register.simple_tag
def afficher_filtre_complet(donnees_filtre, cle):
    """
    Génère le lien pour afficher tous les éléments d'un type donné
    """
    if cle == 'type_article':
        id_type = donnees_filtre['type_article']
        id_categorie = donnees_filtre['categorie']
        
        if id_type == 0:
            resultat = f'<a class="active" href="/article-0-{id_categorie}.html">Tous</a>'
        else:
            resultat = f'<a href="/article-0-{id_categorie}.html">Tous</a>'
    else:
        id_type = donnees_filtre['categorie']
        id_categorie = donnees_filtre['type_article']
        
        if id_type == 0:
            resultat = f'<a class="active" href="/article-{id_categorie}-0.html">Tous</a>'
        else:
            resultat = f'<a href="/article-{id_categorie}-0.html">Tous</a>'
    
    return mark_safe(resultat)

@register.simple_tag
def afficher_types_article(liste_types, donnees_filtre):
    """
    Génère les liens pour chaque type d'article disponible
    """
    liens = []
    
    for type_article in liste_types:
        if type_article[0] == donnees_filtre['type_article']:
            lien = f'<a categorie="" class="active" href="/article-{type_article[0]}-{donnees_filtre[">{type_article[1]}</a>'
        else:
            lien = f'<a categorie="" href="/article-{type_article[0]}-{donnees_filtre[">{type_article[1]}</a>'
        
        liens.append(lien)
    
    return mark_safe(''.join(liens))

Modèles Django pour la gestion de contenu

from django.db import models

class Categorie(models.Model):
    nom = models.CharField(max_length=16)
    description = models.TextField(blank=True)
    
    def __str__(self):
        return self.nom

class Article(models.Model):
    titre = models.CharField(max_length=32)
    contenu = models.TextField()
    categorie = models.ForeignKey(Categorie, on_delete=models.CASCADE)
    
    # Choix pour le type d'article
    TYPES_ARTICLES = (
        (1, 'Python'),
        (2, 'OpenStack'),
        (3, 'Linux'),
        (4, 'DevOps'),
        (5, 'Sécurité'),
    )
    
    type_article = models.IntegerField(choices=TYPES_ARTICLES)
    
    date_publication = models.DateTimeField(auto_now_add=True)
    date_modification = models.DateTimeField(auto_now=True)
    
    def __str__(self):
        return self.titre

Vues Django pour la gestion des articles

from django.shortcuts import render
from django.http import JsonResponse
from . import models

def liste_articles(request, *args, **kwargs):
    """
    Affiche une liste d'articles filtrés par type et catégorie
    """
    # Conversion des paramètres en entiers
    criteres_filtre = {}
    for cle, valeur in kwargs.items():
        kwargs[cle] = int(valeur)
        if valeur != '0':
            criteres_filtre[cle] = valeur
    
    # Récupération des données pour les filtres
    types_articles = models.Article.TYPES_ARTICLES
    categories = models.Categorie.objects.all()
    
    # Filtrage des articles selon les critères
    resultats = models.Article.objects.filter(**criteres_filtre)
    
    return render(
        request,
        'articles/liste.html',
        {
            'resultats': resultats,
            'types_articles': types_articles,
            'categories': categories,
            'parametres_filtre': kwargs
        }
    )

def consommer_api_externes(request):
    """
    Exemple de consommation d'une API externe avec la bibliothèque requests
    """
    import requests
    
    # Effectuer une requête GET vers une API météo
    reponse = requests.get('http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=101121301')
    reponse.encoding = 'utf-8'
    
    # Renvoyer les données à un template pour affichage
    return render(request, 'api/resultats.html', {'donnees_api': reponse.text})

Configuration des URLs

from django.conf.urls import url
from django.contrib import admin
from . import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^api-meteo/$', views.consommer_api_externes, name='api_externes'),
    url(r'^articles/(?P<type_article>\d+)-(?P<categorie>\d+)\.html$', views.liste_articles, name='articles_filtres'),
]</categorie></type_article>

Template pour l'affichage des articles filtrés

{% load filtres_personnalises %}




    <meta charset="UTF-8"></meta>
    <title>Articles - Blog Technique</title>
    <style>
        .filtres a {
            display: inline-block;
            padding: 5px 10px;
            margin: 5px;
            border: 1px solid #ddd;
            border-radius: 3px;
            text-decoration: none;
        }
        .filtres a.active {
            background-color: #007bff;
            color: white;
            border-color: #007bff;
        }
    </style>


    <h1>Blog Technique</h1>
    
    <div class="filtres">
        <div>
            {% afficher_filtre_complet parametres_filtre 'type_article' %}
            {% afficher_types_article types_articles parametres_filtre %}
        </div>
        
        <div>
            {% afficher_filtre_complet parametres_filtre 'categorie' %}
            {% for categorie in categories %}
                {% if categorie.id == parametres_filtre.categorie %}
                    <a class="active" href="/article-{{ parametres_filtre.type_article }}-{{ categorie.id }}.html">{{ categorie.nom }}</a>
                {% else %}
                    <a href="/article-{{ parametres_filtre.type_article }}-{{ categorie.id }}.html">{{ categorie.nom }}</a>
                {% endif %}
            {% endfor %}
        </div>
    </div>
    
    <h2>Résultats de recherche</h2>
    <ul>
        {% for article in resultats %}
            <li>
                <h3>{{ article.titre }}</h3>
                <p>{{ article.contenu|truncatewords:20 }}</p>
                <small>Catégorie : {{ article.categorie.nom }} | Type : {{ article.get_type_article_display }}</small>
            </li>
        {% empty %}
            <li>Aucun article trouvé correspondant à vos critères.</li>
        {% endfor %}
    </ul>


Template pour la démonstration JSONP




    <meta charset="UTF-8"></meta>
    <title>Démonstration JSONP</title>


    <h1>Consommation d'API externe avec JSONP</h1>
    
    <div>
        <h2>Résultat via le serveur Django</h2>
        {{ donnees_api }}
    </div>
    
    <div>
        <h2>Résultat direct via JavaScript</h2>
        <button onclick="obtenirDonnees()">Obtenir les données</button>
        <div id="conteneur"></div>
    </div>
    
    <script src="/static/jquery-3.6.0.min.js"></script>
    <script>
        function obtenirDonnees() {
            // Exemple avec jQuery pour une requête JSONP
            $.ajax({
                url: 'http://www.jxntv.cn/data/jmd-jxtv2.html',
                type: 'GET',
                dataType: 'jsonp',
                jsonp: 'callback',
                jsonpCallback: 'traiterReponse',
                success: function(data) {
                    console.log('Données reçues avec succès:', data);
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    console.error('Erreur lors de la requête:', textStatus, errorThrown);
                }
            });
        }
        
        // Fonction de callback qui sera appelée par le serveur distant
        function traiterReponse(donnees) {
            console.log('Données traitées:', donnees);
            document.getElementById('conteneur').innerHTML = 
                '' + JSON.stringify(donnees, null, 2) + '</script>

'; }

Étiquettes: Django Python WebDev JSONP CMDB

Publié le 21 septembre à 05h14