Formulaires – Documentation

Initialisation des formulaires

Les formulaires sont construits en se basant sur la classe DsfrBaseForm, par exemple :

# votre_app/forms.py

from dsfr.forms import DsfrBaseForm


class ExampleForm(DsfrBaseForm):
    # basic fields
    user_name = forms.CharField(label="Nom d’utilisateur", max_length=100)

    user_email = forms.EmailField(
        label="Adresse électronique",
        help_text="Format attendu : prenom.nom@domaine.fr",
        required=False,
    )

Il est possible de multi-classer :

class AuthorCreateForm(ModelForm, DsfrBaseForm):

Classes CSS

Le formulaire ajoute la ou les classes appropriées (fr-input, fr-select, etc.) en fonction du type de champ, mais uniquement si une classe n’a pas déjà été ajoutée.

Si c’est le cas, il faut soit forcer manuellement les classes à utiliser :

    password = forms.CharField(
        label="Mot de passe", widget=forms.PasswordInput(
            "autocomplete": "current-password",
            "required": True,
            "class": "fr-input my-custom-class"
        )
    )

soit les ajouter dans la méthode init du formulaire (en faisant attention à laisser une espace au début) :

class AuthorCreateForm(ModelForm, DsfrBaseForm):

# [...]

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.fields["password"].widget.attrs["class"] += " my custom class"

Utilisation

La balise {% dsfr_form %} est maintenant dépréciée et sera retirée à la fin de l’année 2024.

Il faut donc remplacer les instances de {% dsfr_form %} par {{ form }} et {% dsfr_form my_custom_form %} par {{ my_custom_form }}.

Composants

dsfr.enums.ExtendedChoices

Extension de Django's models.Choices pour supporter l'ajout d'attributs arbitraires aux enums en utilisant un dictionnaire.

Exemple d'utilisation :

from dsfr.enums import ExtendedChoices

class TemperatureChoices(ExtendedChoices):
  COLD = {
    "value": "COLD",
    "label": "Cold",
    "temperature": "<12°c"
  }
  OK = {
    "value": "OK",
    "label": "ok",
    "temperature": ">=12°c,<25°c"
  }
  HOT = {
    "value": "HOT",
    "label": "Hot!",
    "temperature": ">=25°c"
  }

TemperatureChoices.OK.temperature == ">=12°c,<25°c"

Dans l'exemple précédent, en plus de TemperatureChoices.<enum instance>.value, TemperatureChoices.<enum instance>.label et TemperatureChoices.<enum instance>.name, ExtendedChoices ajoute une propriété temperature pour chaque instance de instance d'enum.

Exemple : TemperatureChoices.<enum instance>.temperature.

Notez que "value" est la seule clé obligatoire dans le dictionnaire. Lorsqu'un dictionnaire ne contient que « value », les exemples suivants sont équivalents :

from django.db import models
from dsfr.enums import ExtendedChoices

class TemperatureChoices(models.Choices):
    COLD = "COLD"

class TemperatureChoices(ExtendedChoices):
    COLD = {"value": "COLD"}

Voir cette section sur la façon de fournir des valeurs par défaut pour des attributs supplémentaires

Utilisation avec enum.auto()

ExtendedChoices supporte l'utilisation de enum.auto() :

from enum import auto
from django.db import models
from dsfr.enums import ExtendedChoices

class TemperatureChoices(ExtendedChoices, models.TextChoices):
  COLD = {
    "value": auto(),
    "label": "Cold",
    "temperature": "<12°c"
  }
  OK = {
    "value": auto(),
    "label": "ok",
    "temperature": ">=12°c,<25°c"
  }
  HOT = {
    "value": auto(),
    "label": "Hot!",
    "temperature": ">=25°c"
  }

ExtendedChoices peut être utilisé en combinaison django.db.models.TextChoices ou django.db.models.IntegerChoices pour calculer automatiquement l'attribut value avec enum.auto() ou peut définir une méthode _generate_next_value_ pour fournir la valeur (voir 2) :

from enum import auto
from dsfr.enums import ExtendedChoices

class TemperatureChoices(ExtendedChoices):
  COLD = {
    "value": auto(),
    "label": "Cold",
    "temperature": "<12°c"
  }
  OK = {
    "value": auto(),
    "label": "ok",
    "temperature": ">=12°c,<25°c"
  }
  HOT = {
    "value": auto(),
    "label": "Hot!",
    "temperature": ">=25°c"
  }

  def _generate_next_value_(name, start, count, last_values):
      return f"{name}: {count}"

Fournir des valeurs par défaut aux attributs supplémentaires

Il peut arriver que vous souhaitiez fournir dynamiquement des valeurs pour un attribut supplémentaire. Si vous ne spécifiez pas de valeur pour un attribut supplémentaire lors de la déclaration de l'enum, vous pouvez la fournir dynamiquement avec la méthode dynamic_attribute_value :

from enum import auto
from django.conf import settings
from django.db import models
from dsfr.enums import ExtendedChoices
class TemperatureChoices(ExtendedChoices, models.IntegerChoices):
    COLD = {
        "value": auto(),
        "temperature": {"lorem": "ipsum 1"},
    }
    OK = auto()

    def dynamic_attribute_value(self, name):
      if name == "temperature":
        return settings.TEMPERATURES[self.value]
      else:
        return -1

Dans l'exemple précédent, la valeur de temperature n'est pas spécifiée pour TemperatureChoices.OK. Accéder à la propriété TemperatureChoices.OK.temperature appellera TemperatureChoices.OK.dynamic_attribute_value("temperature") pour .

Utilisation avancée

Par défaut, lorsque vous spécifiez des attibuts supplémentaires, ExtendedChoices stocke cette valeur dans un membre de l'instance. Le nom de se membre correspond au nom de l'attribut spécifié précédé de __. Exemple :

from enum import auto
from dsfr.enums import ExtendedChoices

class TemperatureChoices(ExtendedChoices):
  COLD = {
    "value": auto(),
    "label": "Cold",
    "temperature": "<12°c"
  }
  OK = {
    "value": auto(),
    "label": "ok",
    "temperature": ">=12°c,<25°c"
  }
  HOT = {
    "value": auto(),
    "label": "Hot!",
    "temperature": ">=25°c"
  }

TemperatureChoices.OK.__temperature == TemperatureChoices.OK.temperature

Si, pour quelque raison que ce soit, vous souhaitez utiliser un autre nom pour l'instance qui stocke la valeur, vous pouvez déclarer une méthode statique private_variable_name :

from enum import auto
from dsfr.enums import ExtendedChoices

class TemperatureChoices(ExtendedChoices):
  COLD = {
    "value": auto(),
    "label": "Cold",
    "temperature": "<12°c"
  }
  OK = {
    "value": auto(),
    "label": "ok",
    "temperature": ">=12°c,<25°c"
  }
  HOT = {
    "value": auto(),
    "label": "Hot!",
    "temperature": ">=25°c"
  }

  @staticmethod
  def private_variable_name(name):
    return f"m_{name}"

TemperatureChoices.OK.m_temperature == TemperatureChoices.OK.temperature

dsfr.enums.RichRadioButtonChoices

Version spécialisée de RichRadioButtonChoices à utiliser avec dsfr.widgets.RichRadioSelect. Cette version déclare en plus les propriétés pictogram, pictogram_alt et html_label :

from enum import auto
from django.db.models import IntegerChoices
from dsfr.utils import lazy_static
from dsfr.enums import RichRadioButtonChoices

class ExampleRichChoices(IntegerChoices, RichRadioButtonChoices):
  ITEM_1 = {
      "value": auto(),
      "label": "Item 1",
      "html_label": "<strong>Item 1</strong>",
      "pictogram": lazy_static("img/placeholder.1x1.png"),
  }
  ITEM_2 = {
      "value": auto(),
      "label": "Item 2",
      "html_label": "<strong>Item 2</strong>",
      "pictogram": lazy_static("img/placeholder.1x1.png"),
  }
  ITEM_3 = {
      "value": auto(),
      "label": "Item 3",
      "html_label": "<strong>Item 3</strong>",
      "pictogram": lazy_static("img/placeholder.1x1.png"),
  }

Voir dsfr.widgets.RichRadioSelect pour plus de détails.

dsfr.widgets.RichRadioSelect

Widget permettant de produire des boutons radio riches. Ce widget fonctionne avec dsfr.enums.ExtendedChoices.

RichRadioSelect.__init__ prend obligatoirement un argument rich_choices de type RichRadioButtonChoices.

Utilisation :

from enum import auto
from django.db.models import IntegerChoices
from django import forms
from dsfr.forms import DsfrBaseForm
from dsfr.utils import lazy_static
from dsfr.enums import RichRadioButtonChoices
from dsfr.widgets import RichRadioSelect

class ExampleRichChoices(RichRadioButtonChoices, IntegerChoices):
    ITEM_1 = {
        "value": auto(),
        "label": "Item 1",
        "html_label": "<strong>Item 1</strong>",
        "pictogram": lazy_static("img/placeholder.1x1.png"),
    }
    ITEM_2 = {
        "value": auto(),
        "label": "Item 2",
        "html_label": "<strong>Item 2</strong>",
        "pictogram": lazy_static("img/placeholder.1x1.png"),
    }
    ITEM_3 = {
        "value": auto(),
        "label": "Item 3",
        "html_label": "<strong>Item 3</strong>",
        "pictogram": lazy_static("img/placeholder.1x1.png"),
    }

class ExampleForm(DsfrBaseForm):
    sample_rich_radio = forms.ChoiceField(
        label="Cases à cocher",
        required=False,
        choices=ExampleRichChoices.choices,
        help_text="Exemple de boutons radios riches",
        widget=RichRadioSelect(rich_choices=ExampleRichChoices),
    )

html_label

L'attribut html_label peut-être utilisé pour déclarer du HTML à insérer dans <label>. Le code est automatiquement marqué sûr avec django.utils.safestring.mark_safe et ne produira pas de problème d'échappement du HTML dans vos templates.

Si html_label n'est pas déclaré par un membre de l'enum, la propriété html_label renvoie la valeur de la propriété label à la place.

pictogram

L'attribut pictogram peut être utilisé pour spécifier le pictogramme du bouton radio riche. Il peut être utilisé en combinaison avec dsfr.utils.lazy_static pour charger une ressource statique.

pictogram_alt

L'attribut pictogram_alt définit la valeur à mettre dans l'attribut alt de la balise <img> utilisée dans le bouton radio riche. S'il n'est pas déclaré par l'enum, RichRadioSelect ajoute un alt="".

dsfr.utils.lazy_static

Équivalent du tag Django {% static %} à utiliser dans le code. Exemple :

from dsfr.utils import lazy_static

lazy_static("img/logo.png")