Regardons un peu les formulaires Django. Et plus particulièrement les ChoiceField.
Avec ceux-ci, nous plaçons une liste déroulante dans notre formulaire.

champ = forms.ChoiceField(choices=((1, 'valeur'), (2, 'seconde_valeur')))

Nous créons ici un ChoiceField dans notre formulaire en y placant également deux valeurs avec les identifiants 1 et 2.

C’est sympa. Mais ça serait mieux de pouvoir récupérer les valeurs de notre base. Gogo alors !

champ = forms.ChoiceField(choices=[[r.id,r.name] for r in Model.objects.all()])

Pour définir les valeurs, nous récupérons toutes les données disponibles avec le modèle « Model » et plaçons les champs id et name. Vous avez donc un ChoiceField dynamique.

Malheureusement (ou heureusement, cela dépends du point de vue), Django place le contenu des formulaires en cache. La requête SQL de récupération de vos modèles ne sera donc executée que si ceux-ci sont modifiés.
Dans un usage basique, cela suffit amplement. Si c’est le cas, je vous invite fortement à conserver cette méthode car ce que j’explique plus bas n’est absolument pas DRY.

Dans mon cas par exemple, j’ai une condition sur mon modèle. Le site est multilingue et je cherche à ne récupérer que les uplets relatifs à la langue en cours.
Avec la mise en cache donc, je n’ai pas les bons éléments car Django ne fait pas particulièrement attention au fait que j’ai changé la requête entre deux (notemment parce que la condition n’est pas dans la requête mais au niveau supérieur, dans le manager).

Je ne peux donc ajouter les éléments directement depuis le formulaire.
Je définis mon champ dans mon formulaire :

champ = model.ChoiceField()

Et dans mon contrôleur (oui c’est pas DRY je l’ai déjà dit) :

if request.method == 'POST':
    form = MyForm(request.POST)
    form.fields['champ'].choices = [[r.id,r.name] for r in Model.objects.all()]
else:
    form = MyForm()
    form.fields['champ'].choices = [[r.id,r.name] for r in Model.objects.all()]

Mon formulaire est correctement défini et avec les bonnes valeurs :)

Et vous, avez-vous déjà eu un problème similaire de cache sur des ChoiceFields de Django ? Avez-vous résolu le problème de manière similaire ou avez-vous réussi à faire quelque chose de plus propre ?

Si vous avez déjà touché à Zend Framework, vous vous êtes peut-être déjà pris la tête sur ses formulaires.
Et dans ce cas la, vous comprenez déjà probablement ce que je veux dire avec ce titre. Sinon, la suite détaille.

Voici un exemple de formulaire un petit peu complexe.
Vous pouvez en voir le résultat sur la page de gestion des mots clés de RefStats.

Comme vous pouvez le constater, il est assez difficile de comprendre l’intérêt d’un tel formulaire, ce qu’il affichera et même les champs qu’il contient.

Le problème des formulaires ZF est le même que dans beaucoup d’autres composants du framework : ils veulent trop en faire.
Le but d’un formulaire côté serveur est pourtant simple : faciliter la validation des données.
Que cela soit dans Rails ou ceux-ci sont directement inclus dans les modèles ou dans Django et Symfony ou ceux-ci ne servent que à faire la validation, à chaque fois, les formulaires ne génèrent pas d’HTML automatiquement.

C’est, malheureusement, ce qu’essaye de faire Zend Framework.
Mais cela rends la chose illisible car pas ergonomique; impossible à modifier pour la première raison.
Et surtout, absolument pas DRY. Si vous avez besoin de placer deux fois le même formulaire pour les mêmes données. Mais les afficher différemment ou simplement ne pas permettre la modification de certaines données, il vous faut créer deux formulaires différents !
Alors qu’il serait tellement plus simple de ne faire qu’un seul formulaire mais deux pages HTML affichant celui-ci.

Du coup comme vous l’avez deviné, je ne vous conseille absolument pas d’utiliser ces formulaires.
Après, au niveau alternatives, je vous épargnerai Rails et Django. Tout autant que Symfony, qui requiert l’inclusion complète du framework.
En revanche Loic me souffle sur Twitter que JForms serait pas mal.
Mais il est l’un des développeurs du framework et je n’ai pas eu l’occasion de tester la chose. Donc vous êtes sans filet !

 
Fork me on GitHub