kpsul/utils/forms.py
Aurélien Delobelle ded824bddd Cleaner use of Group in kfet app
KFetGroup model
- Provides a distinction from non-kfet Groups.
- Convert code appropriately.
- Initially filled from Groups containing K-Fêt (this was the previous
distinction) in the kfetauth.0002 migration.

Permission proxy model (kfetauth app)
- Proxy of the django.contrib.auth Permission model.
- Adds the 'kfet' manager which returns only kfet-related permissions.

KeepUnselectableModelFormMixin
- Helps to keep the unselectable items of many-to-many field for
ModelForm.
- 'kfetauth' forms (related to KFetGroup) use this mixin.

Using KFetGroup allows to simplify the 'kfet/account_group_form.html' template.

A bug is also fixed in 'kfet/form_field_snippet.html', which could lead to
prevent field displays if they used CheckboxSelectMultiple widget.
2017-09-29 22:37:30 +02:00

64 lines
2.2 KiB
Python

class KeepUnselectableModelFormMixin:
"""
Keep unselectable items of queryset-based fields.
Mixin for 'ModelForm'.
Attribute
keep_unselectable_fields (list of field names)
This adding is performed in 'save' method. Specifically, if 'commit' arg
is False, it's done in 'save_m2m' method.
These fields must have a 'queryset' attribute (the selectable items), like
'ModelMultipleChoiceField' (default field for ManyToMany model fields).
"""
keep_unselectable_fields = []
def get_unselectable(self, field_name):
"""
Returns 'field_name' model field items of instance which can't be
selected with the corresponding form field.
Should be used before 'form.save' call, or 'form.save_m2m' call if
'commit=False' was passed as argument to 'form.save'.
"""
if self.instance.pk:
previous = getattr(self.instance, field_name).all()
selectable = self.fields[field_name].queryset
if callable(selectable):
selectable = selectable()
return previous.exclude(pk__in=[o.pk for o in selectable])
else:
# Instance is being created, there is no previous item.
return []
def save(self, commit=True):
# Use 'commit=False' to get the 'save_m2m' method.
instance = super().save(commit=False)
_save_m2m = self.save_m2m
def save_m2m():
# Get the unselectable items.
# Force evaluate because those items are going to change.
unselectable_f = {
f_name: list(self.get_unselectable(f_name))
for f_name in self.keep_unselectable_fields
}
# Default 'save_m2m' use 'set' method of m2m relationships with
# fields' cleaned data.
_save_m2m()
# Add the unselectable elements.
for f_name, unselectable in unselectable_f.items():
getattr(instance, f_name).add(*unselectable)
# Implement the default behavior of 'save' method, with our own
# 'save_m2m'.
if commit:
instance.save()
save_m2m()
else:
self.save_m2m = save_m2m
return instance