L'application mayan/apps/forms/ est la couche de formulaires commune de Mayan EDMS. Elle étend les formulaires Django standard avec des fieldsets, des champs dynamiques, des champs filtrés par ACL, et des widgets personnalisés. Toutes les autres apps utilisent ces classes à la place des classes Django natives.
Note : le label Django de l'app est
mayan_forms(et nonforms) pour éviter un conflit avec l'app interne Django.
DjangoForm
└── FormMixinFieldsets (fieldsets + FormMeta hérité)
└── Form ← formulaire Mayan de base
├── ChoiceForm ← sélection côte-à-côte (many-to-many)
├── DynamicForm ← champs construits depuis un schéma JSON
├── FileDisplayForm ← affichage lecture seule d'un fichier texte
├── FilteredSelectionForm← sélection filtrée par ACL
└── RelationshipForm ← gestion de relation (radio: add/remove/none)
DjangoModelForm
└── FormMixinFieldsets
└── ModelForm ← ModelForm Mayan de base
├── DetailForm ← affichage lecture seule avec extra_fields
└── DynamicModelForm ← ModelForm + champs dynamiques
FormMixinFieldsetsAjoute le support des fieldsets : regroupements nommés de champs affichés dans des sections séparées dans les templates.
class MyForm(Form):
class Meta:
fieldsets = (
('Informations', {'fields': ('name', 'email')}),
('Options', {'fields': ('is_active',)}),
)
Meta.fieldsets ou passés à l'initialisation.ImproperlyConfigured est levée.FormMixinFormMetaFusionne les classes Meta de toute la chaîne MRO, permettant l'héritage des options de formulaire entre classes parentes et enfantes (même principe que le metaclass Django, mais pour les options arbitraires).
FormMixinDynamicFieldsConstruit les champs du formulaire depuis un schéma Python passé à l'initialisation :
schema = {
'fields': {
'nom': {
'class': 'django.forms.CharField',
'label': 'Nom',
'required': True,
'help_text': 'Votre nom complet',
},
'age': {
'class': 'django.forms.IntegerField',
'label': 'Âge',
'default': 18,
}
},
'field_order': ['nom', 'age'],
'fieldsets': (...),
'widgets': {
'nom': {'class': 'django.forms.TextInput', 'kwargs': {'attrs': {'autofocus': True}}}
},
'media': {'js': ['my_script.js']},
}
form = DynamicForm(schema=schema, data=request.POST)
Utilisé notamment pour les backends de source (SourceBackend) dont les paramètres varient selon le type.
FormMixinFilteredFieldsReloadPermet aux champs qui ont une méthode reload() d'être rechargés dynamiquement (utile pour les champs filtrés par ACL quand l'utilisateur change).
DetailFormFormulaire lecture seule qui affiche des champs supplémentaires (extra_fields) calculés à partir de l'instance ou d'une fonction.
class DocumentFileDetailForm(DetailForm):
class Meta:
fields = ('label', 'mimetype')
extra_fields = [
{'field': 'checksum', 'label': 'Checksum'},
{'field': 'size', 'label': 'Taille'},
{'func': lambda obj: obj.get_page_count(), 'label': 'Pages'},
]
Chaque extra_field peut spécifier :
field — attribut ou chemin résolu via resolve_attribute()func — callable appelée avec l'instancelabel, help_text, widget — personnalisation optionnelleobject — objet source (par défaut self.instance)Si la valeur est un QuerySet, un ModelMultipleChoiceField est utilisé. Sinon, un CharField avec PlainWidget (rendu sans balise <input>).
Tous les champs sont marqués readonly.
FilteredSelectionFormFormulaire de sélection filtré par ACL. Les options sont configurables via Meta ou kwargs :
| Option | Description |
|---|---|
model / queryset |
Source des choix |
permission |
Permission ACL pour filtrer le queryset |
user |
Utilisateur pour le filtrage ACL |
allow_multiple |
True → ModelMultipleChoiceField, sinon ModelChoiceField |
field_name |
Nom du champ dans le formulaire |
required |
Champ obligatoire (défaut : True) |
widget_class |
Widget personnalisé |
class DocumentTypeSelectionForm(FilteredSelectionForm):
class Meta:
model = DocumentType
permission = permission_document_create
field_name = 'document_type'
label = _('Type de document')
ChoiceFormFormulaire de sélection côte-à-côte (interface "ajouter / retirer" d'une relation M2M). Contient :
search — champ de filtrage live des optionsselection — MultipleChoiceField avec DisableableSelectWidgetRelationshipFormFormulaire radio (add / remove / none) pour gérer une relation entre deux objets. La méthode save() dispatche vers save_relationship_<type>() selon le choix sélectionné.
FileDisplayFormAffiche le contenu d'un fichier texte (ex. : changelog, licence) dans un TextAreaDiv en lecture seule. Configurer DIRECTORY et FILENAME dans la sous-classe.
FormFieldFilteredModelChoice / FormFieldFilteredModelChoiceMultipleChamps de type ChoiceField dont les options sont filtrées dynamiquement via les ACL Mayan :
field = FormFieldFilteredModelChoice(
source_model=DocumentType,
permission=permission_document_create,
# ou: source_queryset=DocumentType.objects.filter(...)
)
source_queryset ou source_model définissent la source complète.permission + user → filtre via AccessControlList.objects.restrict_queryset().reload() recharge le queryset filtré (utile en cas de changement de contexte).ModelFormFieldFilteredModelMultipleChoiceMême chose mais basé sur ModelMultipleChoiceField (pour les ModelForm).
| Widget | Description |
|---|---|
PlainWidget |
Rend uniquement la valeur brute (sans <input>), utilisé dans DetailForm |
TextAreaDiv |
Simule un <textarea> avec un <div> (scrollable, hauteur adaptative) |
ColorWidget |
<input type="color"> avec sélecteur couleur natif du navigateur |
DisableableSelectWidget |
SelectMultiple avec des options individuellement désactivables |
DropzoneWidget |
Zone de dépôt de fichiers HTML5 (Dropzone.js), utilisé par les sources WebForm |
NamedMultiWidget |
Multi-widget Django avec sous-widgets nommés (dict) au lieu d'indexés |
DropzoneWidgetRendu via forms/forms/widgets/dropzone.html. Intègre la librairie Dropzone.js (exclue du packaging PyPI via static_media_ignore_patterns).
NamedMultiWidgetVariante du MultiWidget Django où chaque sous-widget est identifié par un nom plutôt qu'un index. La méthode value_from_datadict retourne un dictionnaire {nom: valeur}.
class DateRangeWidget(NamedMultiWidget):
subwidgets = {
'start': DateInput,
'end': DateInput,
}
subwidgets_order = ['start', 'end']
TwoStateWidgetWidget de colonne qui affiche une icône verte (ok) ou rouge (fail) selon la véracité de la valeur. Utilisé dans les listes pour indiquer un état booléen.
SourceColumn(
attribute='enabled',
label=_('Activé'),
widget=TwoStateWidget
)
ObjectLinkWidgetWidget de colonne qui rend la valeur comme un lien <a href> vers get_absolute_url() de l'objet. Gère le cas des superusers (lien vers #).
mayan/apps/forms/wizards.py ré-exporte SessionWizardView de django-formtools — utilisé par le wizard de création de documents dans l'app sources.
from mayan.apps.forms.forms import FilteredSelectionForm, DetailForm
from mayan.apps.forms.form_widgets import TwoStateWidget
from mayan.apps.forms.column_widgets import ObjectLinkWidget
# Formulaire de sélection filtré par ACL
class MySelectionForm(FilteredSelectionForm):
class Meta:
model = MyModel
permission = permission_my_view
field_name = 'my_object'
# Formulaire de détail read-only
class MyDetailForm(DetailForm):
class Meta:
model = MyModel
fields = ('name',)
extra_fields = [
{'field': 'computed_property', 'label': _('Valeur calculée')},
]