title: Modélisation d'un workflow linéaire avec Django slug: modelisation-d-un-workflow-lineaire-avec-django date: 2007-03-23 22:55:48 type: post vignette: images/logos/django.png contextual_title1: Sortie de Django 1.0, une année de nouveautés contextual_url1: 20080902-sortie-de-django-10-une-annee-de-nouveautes contextual_title2: Des vacances et des liens contextual_url2: 20071007-des-vacances-et-des-liens contextual_title3: Une solution pour faciliter la conception d'applications web RESTful avec Django contextual_url3: 20070807-une-solution-pour-faciliter-la-conception-d-applications-web-restful-avec-django
Pour le boulot, je devais réaliser un prototype implémentant des processus simples, c'est-à-dire représentés par une suite d'étapes ordonnées. On peut facilement se représenter un tel processus en considérant une ligne de métro avec ses stations sans ramifications.
C'est relativement simple à énoncer mais c'est un peu plus difficile à modéliser, commençons par le modèle de données appliqué à Django (simplifié) :
class Step(models.Model): title = models.CharField() class Process(models.Model): title = models.CharField() class ProcessWorkflow(models.Model): source = models.ForeignKey(Step) target = models.ForeignKey(Step) process = models.ForeignKey(Process)
On dispose donc maintenant d'étapes et de processus. La classe/table ProcessWorkflow permet de lier les étapes entre elles au sein d'un processus.
Une fois ce modèle créé, il s'agit de reconstruire le workflow lorsqu'on en a besoin. Pour cela, un fonction récursive permet assez élégamment de se sortir d'affaire. C'est peut-être ici qu'il peut y avoir mieux en termes de performance mais dans le cadre de mon prototype c'est suffisant :
def build_linear_workflow(items, steps=None, source=None): """ Recursive function to build a linear workflow, return a step list. """ if steps is None: steps = [] for item in items: if item.source == source: if item.target is None: return steps else: steps.append(item.target) return build_linear_workflow(items, steps, item.target)
La fonction est relativement générique car elle me permet de former des workflows d'étapes, de processus, de projets, etc. Je pense qu'elle est relativement facile à comprendre avec le nom des variables choisies (le problème de performance est au niveau de la boucle sur l'ensemble des items, je pense que je pourrais facilement retirer l'item ajouté à steps).
Il ne reste plus qu'à ajouter cette ressource sous la forme d'un propriété au précédent modèle, la classe Process devient alors :
class Process(models.Model): title = models.CharField() def get_workflow(self): return build_linear_workflow(ProcessWorkflow.objects.filter(process=self.id)) workflow = property(get_workflow)
On peut maintenant utiliser directement le workflow dans un template de la façon suivante :
<h3>{{ process.title }}</h3> {% if process.workflow %} <ul> {% for step in process.workflow %} <li>{{ step.title }}</li> {% endfor %} </ul> {% endif %}
Simple comme Django :-).
Voici le fruit de mes recherches, merci à tous ceux qui ont contribué au résultat final, n'hésitez pas à réagir si vous avez une solution plus simple/élégante. La prochaine étape, c'est de passer aux workflows plus complexes, pouvant présenter des arborescences ascendantes et/ou descendantes. Le casse-tête ne fait que commencer...