Vous êtes sur la page 1sur 14

.mobo – Créer un formulaire sous django avec django-crispy-forms http://dotmobo.github.io/django-crispy-forms.

html

.mobo
Blog d'un Pythoniste Djangonaute

   

ACCUEIL CATEGORIES TAGS ARCHIVES

Créer un formulaire sous django avec


django-crispy-forms
Posté le 04/05/2016 dans Django

Django-crispy-forms est une


application django qui va te
permettre de construire,
customiser et réutiliser tes
formulaires en utilisant ton
framework CSS favori. Il permet
d'éviter d'écrire une tonne de
code dans les templates et
applique la philosophie DRY.

Par défaut, il supporte les


frameworks CSS bootstrap,
foundation et uni-form.

On va voir comment tu peux créer un formulaire d'enregistrement compatible avec


bootstrap en utilisant des onglets, un captcha, une liste de pays et un date picker.

Pour simplifier ce tuto, on ne va pas s'occuper de l'internationalisation. Mais rien ne


t'empêches de la mettre en place de ton côté.

1 sur 14 10/12/2019 à 12:28


.mobo – Créer un formulaire sous django avec django-crispy-forms http://dotmobo.github.io/django-crispy-forms.html

1) L'installation et la configuration
C'est parti ! On suppose que tu as déjà installé python 3.5 avec virtualenvwrapper. Tu
crées donc ton environnement virtuel et tu installes les librairies requises:

mkvirtualenv -p /usr/bin/python3.5 demo-django-crispy-forms


pip install Django==1.9.6 django-countries==3.4.1 django-crispy-forms
django-simple-captcha==0.5.1

La version compatible de django-bootstrap3-datetimepicker avec django 1.9 n'est pas


encore disponible sur pypi. Du coup, tu l'installes directement depuis github:

pip install git+https://github.com/nkunihiko/django-bootstrap3-datetimepicker.gi

Tu crées un projet django appelé demo-django-crispy-forms, qui contient une


application core:

django-admin startproject demo_django_crispy_forms


cd demo_django_crispy_forms/demo_django_crispy_forms
mkdir apps && cd apps
django-admin startapp core

Dans le fichier settings.py de ton projet, tu modifies/ajoutes les lignes suivantes:

LANGUAGE_CODE = 'fr'
TIME_ZONE = 'Europe/Paris'
INSTALLED_APPS = (
...
'crispy_forms',
'django_countries',
'bootstrap3_datetime',
'captcha',
'demo_django_crispy_forms.apps.core'
)
CRISPY_TEMPLATE_PACK = 'bootstrap3'

Tu as ainsi défini la langue et le time zone, ajouté les applications installées


précédemment et utilisé le template bootstrap3 pour crispy-forms.

Il te su�it de migrer la base de données et de vérifier que tout est bon pour l'instant.
Depuis le répertoire racine du projet, tu fais:

./manage.py migrate && ./manage.py check

2) Le modèle

2 sur 14 10/12/2019 à 12:28


.mobo – Créer un formulaire sous django avec django-crispy-forms http://dotmobo.github.io/django-crispy-forms.html

Maintenant que tu as fini l'installation et le paramétrage des di�érentes libraires, tu vas


pouvoir créer le modèle de ton inscription.

Dans apps/core/models.py, tu définis le modèle Registration suivant :

from django.db import models


from django_countries.fields import CountryField

class Registration(models.Model):
"""
Modèle de l'inscription
"""
CIVILITY_CHOICES = (
('M.', 'M.'),
('MME', 'Mme')
)
STREET_TYPE_CHOICES = (
('Boulevard', 'Boulevard'),
('Avenue', 'Avenue'),
('Cours', 'Cours'),
('Place', 'Place'),
('Rue', 'Rue'),
('Route', 'Route'),
('Voie', 'Voie'),
('Chemin', 'Chemin'),
('Square', 'Square'),
('Impasse', 'Impasse'),
('Rond-point', 'Rond-point'),
('Quai', 'Quai')
)

civility = models.CharField(max_length=3, choices=CIVILITY_CHOICES


default='M.', verbose_name="Civilité"
birth_name = models.CharField(max_length=255, verbose_name="Nom de naissance
last_name = models.CharField(max_length=255, blank=True, null=True
verbose_name="Nom d'usage ou marital"
first_name = models.CharField(max_length=255, verbose_name="Prénom"
birth_date = models.DateField(verbose_name="Date de naissance ")
birth_place = models.CharField(max_length=255, verbose_name="Ville de naissa
birth_country = CountryField(max_length=255, verbose_name="Pays de naissance
mail = models.EmailField(max_length=255, verbose_name="Mail")
street_type = models.CharField(max_length=30, verbose_name="Type de rue"
choices=STREET_TYPE_CHOICES, default
street_number = models.CharField(max_length=30, verbose_name="Numéro de rue"
street = models.CharField(max_length=30, verbose_name="Rue")
comp_1 = models.CharField(max_length=255, verbose_name="Complément 1"
blank=True, null=True)

3 sur 14 10/12/2019 à 12:28


.mobo – Créer un formulaire sous django avec django-crispy-forms http://dotmobo.github.io/django-crispy-forms.html

comp_2 = models.CharField(max_length=255, verbose_name="Complément 2"


blank=True, null=True)
city = models.CharField(max_length=255, verbose_name="Ville")
zip_code = models.CharField(max_length=255, verbose_name="Code postal"
country = CountryField(max_length=255, verbose_name="Pays")
phone = models.CharField(max_length=255, blank=True, null=True,
verbose_name="Téléphone")
comments = models.TextField(blank=True, null=True, verbose_name=

Pour chaque champ, crispy-forms va :

utiliser le verbose_name comme label.


vérifier les paramètres blank et null pour savoir si le champ est obligatoire.
utiliser le type de champ pour définir le type de la balise <input>.
récupérer les valeurs du paramètre choices (si présent) pour la balise <select>.

Enfin, tu mets à jour la base de données:

./manage.py makemigrations
./manage.py migrate

3) Le formulaire
Place au formulaire. J'ai rajouté des commentaires directement dans le code ci-dessous
pour expliquer les di�érentes étapes.

Dans apps/core/forms.py, tu mets:

from django import forms


from .models import Registration
from crispy_forms.helper import FormHelper
from crispy_forms.bootstrap import StrictButton
from bootstrap3_datetime.widgets import DateTimePicker
from crispy_forms.layout import Layout
from crispy_forms.bootstrap import TabHolder, Tab
from captcha.fields import CaptchaField

class RegistrationForm(forms.ModelForm):
"""
Formulaire d'inscription
"""

# Ici, tu vas rajouter les champs supplémentaires au modèle


# Tu définis le captcha
captcha = CaptchaField()
# Tu ajoutes un mail de confirmation

4 sur 14 10/12/2019 à 12:28


.mobo – Créer un formulaire sous django avec django-crispy-forms http://dotmobo.github.io/django-crispy-forms.html

confirmation_mail = forms.EmailField(label="Mail de confirmation"

def __init__(self, *args, **kwargs):


"""
Surcharge de l'initialisation du formulaire
"""
super().__init__(*args, **kwargs)
# Tu modifies le label de la date de naissance pour rajouter le format
self.fields['birth_date'].label = "%s (JJ/MM/AAAA)" % "Date de naissance
# Tu utilises FormHelper pour customiser ton formulaire
self.helper = FormHelper()
# Tu définis l'id et la classe bootstrap de ton formulaire
self.helper.form_class = 'form-horizontal'
self.helper.form_id = 'registration-form'
# Tu définis la taille des labels et des champs sur la grille
self.helper.label_class = 'col-md-2'
self.helper.field_class = 'col-md-8'
# Tu crées l'affichage de ton formulaire
self.helper.layout = Layout(
# Le formulaire va contenir 3 onglets
TabHolder(
# Premier onglet
Tab(
# Label de l'onglet
'Étape 1 - Identité',
# Liste des champs du modèle à afficher dans l'onglet
'civility',
'birth_name',
'last_name',
'first_name',
'birth_date',
'birth_place',
'birth_country',
# Tu rajoutes un bouton "Suivant"
StrictButton(
'<span class="glyphicon glyphicon-arrow-right"
aria-hidden="true"></span> %s' % "Suivant",
type='button',
css_class='btn-default col-md-offset-9 btnNext'
)

),
# Deuxième onglet
Tab(
# Label de l'onglet
'Étape 2 - Adresse',
# Liste des champs à afficher
'street_number',

5 sur 14 10/12/2019 à 12:28


.mobo – Créer un formulaire sous django avec django-crispy-forms http://dotmobo.github.io/django-crispy-forms.html

'street_type',
'street',
'comp_1',
'comp_2',
'city',
'zip_code',
'country',
'phone',
# Tu rajoutes des boutons "Précédent" et "Suivant"
StrictButton(
'<span class="glyphicon glyphicon-arrow-left"
aria-hidden="true"></span> %s' % 'Précédent'
type='button',
css_class='btn-default btnPrevious',
),
StrictButton(
'<span class="glyphicon glyphicon-arrow-right"
aria-hidden="true"></span> %s' % 'Suivant',
type='button',
css_class='btn-default col-md-offset-8 btnNext'
)
),
# Troisième onglet
Tab(
# Label de l'onglet
'Étape 3 - Validation',
# Liste des champs à afficher dont les champs supplémentaire
'mail',
'confirmation_mail',
'comments',
'captcha',
# Tu rajoutes des boutons "Précédent" et "Valider"
StrictButton(
'<span class="glyphicon glyphicon-arrow-left"
aria-hidden="true"></span> %s' % "Précédent"
type='button',
css_class='btn-default btnPrevious',
),
StrictButton(
'<span class="glyphicon glyphicon-ok" \
aria-hidden="true"></span> %s' % "Valider",
type='submit',
css_class='btn-default col-md-offset-8'
)
),
),
)

6 sur 14 10/12/2019 à 12:28


.mobo – Créer un formulaire sous django avec django-crispy-forms http://dotmobo.github.io/django-crispy-forms.html

def clean_confirmation_mail(self):
"""
Méthode pour vérifier que le mail correspond bien au
mail de confirmation lors de la validation du formulaire
"""
confirmation_mail = self.cleaned_data['confirmation_mail']
mail = self.cleaned_data['mail']
if mail != confirmation_mail:
raise forms.ValidationError(
"Le mail et le mail de confirmation ne sont pas identiques"
return confirmation_mail

class Meta:
# Tu définis le modèle utilisé
model = Registration
exclude = []
# Tu customises le champ date de naissance pour ajouter le date picker
widgets = {
'birth_date': DateTimePicker(
options={"format": "DD/MM/YYYY", "pickTime": False,
"useStrict": True, "viewMode": "years",
"startDate": "01/01/1900"},
attrs={'placeholder': 'ex: 05/11/1975'}
)
}

4) Les vues
Maintenant que tu as ton formulaire, il te faut une vue pour a�icher le formulaire et une
autre pour a�icher un message de confirmation après la validation de celui-ci.

Tu vas créer tout ça dans apps/core/views.py:

from django.views.generic.edit import CreateView


from .models import Registration
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import render
from .forms import RegistrationForm

class RegistrationCreate(CreateView):
"""
Affichage du formulaire
"""
model = Registration
form_class = RegistrationForm
success_url = reverse_lazy('core:success')

7 sur 14 10/12/2019 à 12:28


.mobo – Créer un formulaire sous django avec django-crispy-forms http://dotmobo.github.io/django-crispy-forms.html

def registration_success(request):
"""
Message de confirmation
"""
return render(request, 'core/registration_success.html')

5) Les urls
Dans le fichier des urls du projet, tu vas inclure les urls de l'application core et l'url pour
le captcha.

Dans urls.py, tu insères:

from django.conf.urls import url, include


from django.contrib import admin
from .apps.core.urls import urlpatterns as core_urls

urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^', include(core_urls, namespace='core')),
url(r'^captcha/', include('captcha.urls')),
]

Et dans apps/core/urls.py, tu mets les urls correspondantes à tes deux vues:

from django.conf.urls import patterns, url


from .views import RegistrationCreate, registration_success

urlpatterns = [
url(r'^$', RegistrationCreate.as_view(), name='add'),
url(r'^success/$', registration_success, name='success'),
]

6) Les templates
Tu vas créer trois templates:

Le premier, base.html, qui servira de base aux deux autres.


Le deuxième, core/registration_form.html, pour a�icher le formulaire.
Le dernier, core/registration_success.html, pour a�icher le message de
confirmation.

Le template apps/core/templates/base.html va contenir:

8 sur 14 10/12/2019 à 12:28


.mobo – Créer un formulaire sous django avec django-crispy-forms http://dotmobo.github.io/django-crispy-forms.html

<!DOCTYPE html>
<html lang="fr">
<head>
<title>Mon site</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet"
href="//cdn.jsdelivr.net/bootstrap/3.3.6/css/bootstrap.min.css"
<link rel="stylesheet"
href="//cdn.jsdelivr.net/bootstrap/3.3.6/css/bootstrap-theme.min.css"
<script src="//cdn.jsdelivr.net/jquery/2.2.3/jquery.min.js"></script
{% block head-javascript %}{% endblock %}
</head>
<body>
<div class="container" role="main">
<div id="summary">
<div class="page-header">
{% block page-header %}{% endblock %}
</div>
</div>
{% block content %}
{% endblock %}
</div>
<script src="//cdn.jsdelivr.net/bootstrap/3.3.6/js/bootstrap.min.js"
{% block foot-javascript %}{% endblock %}
</body>
</html>

C'est une page html5 standard, qui contient les fichiers nécessaires à bootstrap, un block
page-header pour a�icher le titre de la page et un block content pour a�icher le
contenu de la page. Les templates suivants vont donc étendre base.html.

Pour le template apps/core/templates/core/registration_form.html, tu mets:

{% extends "base.html" %}
{% load crispy_forms_tags %}

{% block head-javascript %}
{{ form.media }}
{% endblock %}

{% block page-header %}
<h1>Inscription</h1>
{% endblock %}

{% block content %}
<div class="row">{% crispy form %}</div>

9 sur 14 10/12/2019 à 12:28


.mobo – Créer un formulaire sous django avec django-crispy-forms http://dotmobo.github.io/django-crispy-forms.html

{% endblock %}

{% block foot-javascript %}
<script>
$( document ).ready( function() {
// On interdit le copier/coller du mail
$('#id_confirmation_mail').bind('copy paste', function(e) {
e.preventDefault();
});
// On affiche le calendrier lorsqu'on clique sur le champ date de naissance
$('#id_birth_date').click(function(){
$(this).parent().data("DateTimePicker").show();
});
// On teste la valeur de la date et on la force à vide si elle est mauvaise
$('#id_birth_date').change(function(e){
e.preventDefault();
e.stopPropagation();
var val = $(this).val();
var format = new RegExp("^\\d{2}\/\\d{2}\/\\d{4}$");
if(!format.test(val)) {
$(this).val("");
}
});
// Afficher l'onglet suivant en cliquant sur le bouton suivant
$('.btnNext').click(function(){
$('.nav-tabs > .active + li a').trigger('click');
$(".nav-tabs + .tab-content").find(":input:visible:first").focus
});
// Afficher l'onglet précédent en cliquant sur le bouton précédent
$('.btnPrevious').click(function(){
$('.nav-tabs > .active').prev('li').find('a').trigger('click'
$(".nav-tabs + .tab-content").find(":input:visible:first").focus
});
});
</script>
{% endblock %}

Pour crispy-forms, il ne faut que deux éléments ! Le {% load crispy_forms_tags %}, qui
permet d'utiliser le {% crispy form %} pour a�icher le formulaire.

Et c'est tout ! Plutôt cool non ?

Le {{ form.media }} est nécessaire au date picker et tout ce qui se trouve dans le block
foot-javascript permet de pousser un peu plus la customisation.

Enfin, de la même manière, tu crées le template apps/core/templates


/core/registration_success.html:

{% extends "base.html" %}

10 sur 14 10/12/2019 à 12:28


.mobo – Créer un formulaire sous django avec django-crispy-forms http://dotmobo.github.io/django-crispy-forms.html

{% block page-header %}
<h1>Succès</h1>
{% endblock %}

{% block content %}
<div class="row">Votre inscription a été validée.</div>
{% endblock %}

7) La page d'administration
Pour pouvoir vérifier les futures inscriptions, tu ajoutes le modèle Registration dans
apps/core/admin.py:

from django.contrib import admin


from .models import Registration

class RegistrationAdmin(admin.ModelAdmin):
list_display = ('birth_name', 'last_name', 'first_name')

admin.site.register(Registration, RegistrationAdmin)

Tu vas également en profiter pour ajouter un compte admin via:

./manage.py createsuperuser

8) La vérification
Tout est fini ! Il ne reste plus qu'à vérifier que ça fonctionne correctement.

Tu démarres le serveur:

./manage.py runserver

Puis, tu te rends sur http://127.0.0.1:8000 pour tester ton formulaire !

11 sur 14 10/12/2019 à 12:28


.mobo – Créer un formulaire sous django avec django-crispy-forms http://dotmobo.github.io/django-crispy-forms.html

Après avoir correctement saisie et validé le formulaire, rends-toi sur http://127.0.0.1:8000


/admin/core/registration/ pour vérifier ton inscription.

Tu sais désormais utiliser un outil puissant pour générer tes nombreux formulaires ! Et
pour voir le résultat final, tu peux te rendre sur mon dépôt github.

python django formulaire bootstrap crispy forms captcha datetimepicker

12 sur 14 10/12/2019 à 12:28


.mobo – Créer un formulaire sous django avec django-crispy-forms http://dotmobo.github.io/django-crispy-forms.html

8 Commentaires dotmobo 
1 S'identifier

 Recommander 2 t Tw eet f Partager Les meilleurs

Participer à la discussion…

S'IDENTIFIER AVEC
OU INSCRIVEZ-VOUS SUR DISQUS ?

Nom

Georgie Mnemonic • il y a 2 mois ⚑−


Hello, merci pour ton tuto, je suis en train de le tester avec les dernières versions de
DJango/Python, mais je reçois l'erreur suivante:
from django.forms.util import flatatt
ModuleNotFoundError: No module named 'django.forms.util'
J'ai testé 2 ou 3 trucs, mais sans succès, une idée ?
△ ▽ • Répondre • Partager ›

dotmobo Modo > Georgie Mnemonic • il y a 2 mois ⚑ −


L'article a deux ans donc j'imagine que certaines librairies django ont du
évoluer, il faut surement que tu corriges les imports avec la nouvelle version
△ ▽ • Répondre • Partager ›

Georgie Mnemonic > dotmobo • il y a 2 mois ⚑−


Effectivement. Il semble que ce problème était lié au datapicker de
bootstrap3.
Appliquer ceci :
https://pypi.org/project/dj...
et passer sur bootstrap4 corrige l'erreur et me permet de poursuivre
le tuto.
THX
△ ▽ • Répondre • Partager ›

Toure Toure • il y a 2 ans − ⚑


salut merci ton projet, j'ai suivi ton tuto, j'utilise python 3.6.4 et django 2.0.2, je recois
l'erreur suivant "JsFiles' object is not reversible" qd je lance la page
http://127.0.0.1:8000 que faire merci
△ ▽ • Répondre • Partager ›

Joachim Adou Kre • il y a 2 ans − ⚑


Félicitations pour ce travail. Comment pourrais-je mettre les boutons d'ajout et
modifier pour civilité. Merci
△ ▽ • Répondre • Partager ›

13 sur 14 10/12/2019 à 12:28


.mobo – Créer un formulaire sous django avec django-crispy-forms http://dotmobo.github.io/django-crispy-forms.html

© 2018 - This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License
Propulsé par Pelican - Flex thème par Alexandre Vicenzi

14 sur 14 10/12/2019 à 12:28

Vous aimerez peut-être aussi