M2M form mixin
This commit is contained in:
parent
e92d50593c
commit
6f5fa19fc3
1 changed files with 50 additions and 0 deletions
50
shared/forms.py
Normal file
50
shared/forms.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
from django.forms.models import ModelForm
|
||||
|
||||
|
||||
class ProtectedModelForm(ModelForm):
|
||||
"""
|
||||
Extension de `ModelForm`
|
||||
|
||||
Quand on save un champ `ManyToMany` dans un `ModelForm`, la méthode appelée
|
||||
est <field>.set(), qui écrase l'intégralité du contenu.
|
||||
Le problème survient quand le `field` a un queryset restreint, et qu'on ne
|
||||
veut pas toucher aux choix qui ne sont pas dans ce queryset...
|
||||
C'est le but de ce mixin.
|
||||
|
||||
Attributs :
|
||||
- `protected_fields` : champs qu'on souhaite protéger.
|
||||
"""
|
||||
|
||||
protected_fields = []
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
for field_name in self.protected_fields:
|
||||
if field_name not in self.fields:
|
||||
raise ValueError("Le champ %s n'existe pas !" % field_name)
|
||||
|
||||
def _get_protected_elts(self, field_name):
|
||||
"""
|
||||
Renvoie tous les éléments de `instance.<field_name>` qui ne sont pas
|
||||
dans `self.<field_name>.queryset` (et sont donc à conserver).
|
||||
|
||||
NB : on "désordonne" tous les querysets via `.order_by()` car Django
|
||||
ne peut pas effectuer une union de QS ordonnés.
|
||||
"""
|
||||
if self.instance.pk:
|
||||
previous = getattr(self.instance, field_name).order_by()
|
||||
selectable = self.fields[field_name].queryset.order_by()
|
||||
return previous.difference(selectable)
|
||||
else:
|
||||
# Nouvelle instance, rien à protéger.
|
||||
return self.fields[field_name].queryset.none()
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
for field_name in self.protected_fields:
|
||||
selected_elts = cleaned_data[field_name].order_by()
|
||||
protected_elts = self._get_protected_elts(field_name)
|
||||
cleaned_data[field_name] = selected_elts.union(protected_elts)
|
||||
|
||||
return cleaned_data
|
Loading…
Reference in a new issue