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 transactionpour 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
- 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é
- 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
- 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
- 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>
'; }