Project Oxygen & Ideo-LabIDEO LAB Dashboard 2026

Data Models & IA

Utiliser l’IA pour concevoir, documenter et sécuriser les modèles Django : normalisation, PII, qualité des données, embeddings, migrations & index.

Django ORM · pgvector · Validators · Admin ROI: élevé (réduction bugs & dette) Effort: moyen Complexité: moyenne
Architecture de référence
Modèles Django ←→ (Lint/Doctor IA) ←→ Suggestions (index, uniques, types, PII)
→ Génération contrôlée (diff/migration) → Tests/CI → Admin (badges PII & qualité)
→ Option: champs d’embedding + signaux pour MAJ vectorielle

Quick Wins

  • Lister les champs candidats à UNIQUE/INDEX (email, slug, (user, date)).
  • Ajouter des validators/constraints au niveau DB (CheckConstraint, UniqueConstraint).
  • Taguer PII par champ et activer un ‘scrub’ avant log/indexation.

Pièges & Anti-patterns

  • Laisser l’IA modifier le schéma sans validation humaine.
  • PIOU : PII en clair dans les logs, fixtures, exports.
  • Oublier les migrations inverses / data-migrations de nettoyage.

Rappels pragmatiques + squelette prêt à l’emploi.

  • Clés naturelles lisibles (slug) + UNIQUE; ForeignKey avec on_delete explicite.
  • INDEX sur colonnes de filtre/join, CompositeIndex pour (user, created_at).
  • CheckConstraint pour règles métiers simples (montant>=0, date_end>=date_start).
models.py — exemple e-commerce compact python
from django.db import models

class Product(models.Model):
    sku = models.CharField(max_length=40, unique=True)
    name = models.CharField(max_length=200)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    class Meta:
        indexes = [models.Index(fields=['name'])]

class Order(models.Model):
    user = models.ForeignKey('auth.User', on_delete=models.PROTECT)
    created_at = models.DateTimeField(auto_now_add=True)
    class Meta:
        indexes = [models.Index(fields=['user','created_at'])]
        constraints = [models.CheckConstraint(check=models.Q(created_at__isnull=False), name='order_ts_not_null')]

class OrderItem(models.Model):
    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items')
    product = models.ForeignKey(Product, on_delete=models.PROTECT)
    qty = models.PositiveIntegerField(default=1)
    class Meta:
        unique_together = [('order','product')]
KPIs à suivre
  • Taux de requêtes ‘Full Scan’ → doit baisser
  • Nb d’anomalies CheckConstraint / 1k enregistrements
  • Latence p95 requêtes clés après indexation

Déclarer les PII au niveau modèle et les rendre visibles dans l’admin.

  • Dictionnaire PII par modèle (email, phone, iban, dob...).
  • Mixin Admin qui affiche un badge PII et empêche l’export brut.
  • Fonction de ‘scrub’ pour logs & exports (hash/masque).
models.py — annotation PII python
class Customer(models.Model):
    email = models.EmailField(unique=True)
    phone = models.CharField(max_length=20, blank=True)
    date_of_birth = models.DateField(null=True, blank=True)
    PII = {'email':'contact', 'phone':'contact', 'date_of_birth':'personal'}
admin.py — mixin avec badges & blocage export python
from django.contrib import admin
from django.utils.html import format_html

class PIIAdminMixin:
    pii_icon = '<span class="badge bg-danger ms-2">PII</span>'
    def get_list_display(self, request):
        base = super().get_list_display(request)
        return base + ('_pii',)
    def _pii(self, obj):
        return format_html(self.pii_icon) if getattr(obj.__class__, 'PII', None) else ''

def scrub(val:str)->str:
    if not val: return val
    return val[:2] + '***' + val[-2:]
KPIs à suivre
  • % champs PII correctement annotés
  • Nb d’exports bloqués/corrigés
  • Incidents liés à la fuite PII (=0)

Ajouter un champ vectoriel et le maintenir à jour lorsqu’un texte change.

  • VectorField(dim=1536) pour titres/descriptions.
  • Signal post_save pour recalcul asynchrone (celery, RQ) ou synchrone simple.
  • Index IVFFLAT/HNSW + opérateur <=> pour la recherche.
models.py — champ vectoriel + signal python
from pgvector.django import VectorField
from django.db.models.signals import post_save
from django.dispatch import receiver

class Article(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    embedding = VectorField(dimensions=1536, null=True)

@receiver(post_save, sender=Article)
def update_embedding(sender, instance, **kwargs):
    text = (instance.title or '') + '\n' + (instance.body or '')
    # emb = openai_embed(text)  # à implémenter
    emb = [0.0]*1536
    if emb:
        type(instance).objects.filter(pk=instance.pk).update(embedding=emb)
SQL — index vectoriel sql
CREATE INDEX IF NOT EXISTS article_embedding_ivf
ON article USING ivfflat (embedding vector_cosine_ops) WITH (lists=100);
KPIs à suivre
  • Couverture embedding (% non-null)
  • Latence kNN p95
  • Taux de clic sur résultats sémantiques

Un ‘doctor’ qui scanne les modèles et propose des corrections (indexes, uniques, validators).

  • Lister champs candidats à INDEX / UNIQUE / NOT NULL.
  • Détecter nombres/monnaies stockés en float → proposer Decimal.
  • Détecter FK sans on_delete explicite.
management command — model_doctor python
from django.core.management.base import BaseCommand
from django.apps import apps
from django.db import models

class Command(BaseCommand):
    help = 'Audit des modèles et suggestions (indexes, uniques, types, FK).'

    def handle(self, *args, **opts):
        for model in apps.get_models():
            fields = {f.name: f for f in model._meta.get_fields() if isinstance(f, models.Field)}
            if 'email' in fields and not fields['email'].unique:
                self.stdout.write(f'[SUGGEST] {model.__name__}.email → unique=True')
            for f in fields.values():
                if isinstance(f, models.FloatField) and 'price' in f.name:
                    self.stdout.write(f'[SUGGEST] {model.__name__}.{f.name} → DecimalField')
KPIs à suivre
  • Nb de suggestions appliquées / sprint
  • Réduction d’erreurs d’intégrité
  • Score de couverture des bonnes pratiques

L’IA propose; l’humain valide. Générer un ‘diff’ explicite et des migrations idempotentes.

  • Toujours sortir une proposition **diff lisible** → revue humaine obligatoire.
  • Data migrations séparées (nettoyage, backfill) + tests.
  • Rollback décrit (reverse_code pour RunPython).
ex. migration d’index composite python
from django.db import migrations, models

class Migration(migrations.Migration):
    dependencies = []
    operations = [
        migrations.AddIndex(
            model_name='order',
            index=models.Index(fields=['user','created_at'], name='order_user_ts'),
        )
    ]
KPIs à suivre
  • Durée moyenne migration en prod
  • Nb de rollback/s
  • Latence clés post-déploiement
Prochaines étapes : Brancher un vrai service d’embeddings, intégrer le ‘model_doctor’ à la CI, et afficher un rapport de qualité des modèles dans l’admin.