Merge pull request #23 from mariuccio/master

select_multiple_with_max plugin
This commit is contained in:
Artur Barseghyan 2016-05-12 16:21:16 +02:00
commit 9cf0fc13f6
9 changed files with 357 additions and 0 deletions

View file

@ -0,0 +1,79 @@
=========================================================
fobi.contrib.plugins.form_elements.fields.select_multiple_with_max
=========================================================
A ``Fobi`` Select Multiple form field plugin with max choices. Makes use of the
``django.forms.widgets.SelectMultiple``.
Installation
===============================================
1. Add ``fobi.contrib.plugins.form_elements.fields.select_multiple_with_max`` to the
``INSTALLED_APPS`` in your ``settings.py``.
.. code-block:: python
INSTALLED_APPS = (
# ...
'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max',
# ...
)
2. In the terminal type:
.. code-block:: none
$ ./manage.py fobi_sync_plugins
3. Assign appropriate permissions to the target users/groups to be using
the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True.
4. By default, the submitted form value of `select_multiple_with_max`
elements is label (human readable representation of the value chosen).
However, that part of the bahaviour has been made configurable. You can
choose between the following options:
Consider the following list of (value, label) choices (the first element in
the tuple is value, the second element is label):
.. code-block:: python
[
('alpha', 'Alpha'),
('beta', 'Beta'),
('gamma', 'Gamma'),
]
- "val": `value` (example: "alpha").
- "repr" (default): `label` (example: "Alpha").
- "mix": `value (label)` (examle: "Alpha (alpha)").
Simply set the
``FOBI_FORM_ELEMENT_SELECT_MULTIPLE_WITH_MAX_SUBMIT_VALUE_AS`` assign one of the
following values: "val", "repr" or "mix" to get the desired behaviour.
Usage
===============================================
You should be entering a single choice per line. Choice might
consist of just a single value or value/label pair. If you enter an integer in
the 'max_choices' field, the user can choose only <max_choices> or less choices.
For example:
.. code-block:: none
1
2
alpha, Alpha
beta, Beta
omega
The following HTML would be made of:
.. code-block:: html
<select id="id_NAME_OF_THE_ELEMENT" name="NAME_OF_THE_ELEMENT">
<option value="1">1</option>
<option value="2">2</option>
<option value="alpha">Alpha</option>
<option value="beta">Beta</option>
<option value="omega">omega</option>
</select>

View file

@ -0,0 +1,9 @@
__title__ = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = 'Copyright (c) 2014 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('default_app_config', 'UID',)
default_app_config = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max.apps.Config'
UID = 'select_multiple_with_max'

View file

@ -0,0 +1,15 @@
__title__ = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max.apps'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = 'Copyright (c) 2014 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('Config',)
try:
from django.apps import AppConfig
class Config(AppConfig):
name = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max'
label = 'fobi_contrib_plugins_form_elements_fields_select_multiple_with_max'
except ImportError:
pass

View file

@ -0,0 +1,29 @@
__title__ = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max.conf'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = 'Copyright (c) 2014-2015 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('get_setting',)
from django.conf import settings
from . import defaults
def get_setting(setting, override=None):
"""
Get a setting from
`fobi.contrib.plugins.form_elements.fields.select_multiple_with_max` conf module,
falling back to the default.
If override is not None, it will be used instead of the setting.
:param setting: String with setting name
:param override: Value to use when no setting is available. Defaults
to None.
:return: Setting value.
"""
if override is not None:
return override
if hasattr(settings, 'FOBI_FORM_ELEMENT_SELECT_MULTIPLE_WITH_MAX_{0}'.format(setting)):
return getattr(settings, 'FOBI_FORM_ELEMENT_SELECT_MULTIPLE_WITH_MAX_{0}'.format(setting))
else:
return getattr(defaults, setting)

View file

@ -0,0 +1,9 @@
__title__ = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max.defaults'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = 'Copyright (c) 2014-2015 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('SUBMIT_VALUE_AS',)
from fobi.constants import SUBMIT_VALUE_AS_REPR
SUBMIT_VALUE_AS = SUBMIT_VALUE_AS_REPR

View file

@ -0,0 +1,21 @@
from django.forms.fields import MultipleChoiceField
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
class MultipleChoiceWithMaxField(MultipleChoiceField):
def __init__(self, max_choices=None, choices=(), required=True, widget=None,
label=None, initial=None, help_text='', *args, **kwargs):
super(MultipleChoiceWithMaxField, self).__init__(
choices=choices, required=required, widget=widget, label=label,
initial=initial, help_text=help_text, *args, **kwargs
)
self.max_choices = max_choices
def validate(self, value):
super(MultipleChoiceWithMaxField, self).validate(value)
if self.max_choices:
if len(value) > self.max_choices:
raise ValidationError(_("You must choose no more than {0} "
"values.".format(self.max_choices)))

View file

@ -0,0 +1,97 @@
__title__ = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max.fobi_form_elements'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = 'Copyright (c) 2014 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('SelectMultipleWithMaxInputPlugin',)
from django.forms.widgets import SelectMultiple
from django.utils.translation import ugettext_lazy as _
from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme
from fobi.constants import (
SUBMIT_VALUE_AS_VAL, SUBMIT_VALUE_AS_REPR
)
from fobi.helpers import get_select_field_choices, safe_text
from . import UID
from .fields import MultipleChoiceWithMaxField
from .forms import SelectMultipleWithMaxInputForm
from .settings import SUBMIT_VALUE_AS
theme = get_theme(request=None, as_instance=True)
class SelectMultipleWithMaxInputPlugin(FormFieldPlugin):
"""
Select multiple field plugin with max.
"""
uid = UID
name = _("Select multiple with max")
group = _("Fields")
form = SelectMultipleWithMaxInputForm
def get_form_field_instances(self, request=None):
"""
Get form field instances.
"""
choices = get_select_field_choices(self.data.choices)
kwargs = {
'label': self.data.label,
'help_text': self.data.help_text,
'initial': self.data.initial,
'required': self.data.required,
'choices': choices,
'widget': SelectMultiple(attrs={'class': theme.form_element_html_class}),
}
if self.data.max_choices:
kwargs['max_choices'] = self.data.max_choices
return [(self.data.name, MultipleChoiceWithMaxField, kwargs)]
def submit_plugin_form_data(self, form_entry, request, form):
"""
Submit plugin form data/process.
:param fobi.models.FormEntry form_entry: Instance of
``fobi.models.FormEntry``.
:param django.http.HttpRequest request:
:param django.forms.Form form:
"""
# In case if we should submit value as is, we don't return anything.
# In other cases, we proceed further.
if SUBMIT_VALUE_AS != SUBMIT_VALUE_AS_VAL:
# Get the object
values = form.cleaned_data.get(self.data.name, None)
# Get choices
choices = dict(get_select_field_choices(self.data.choices))
# Returned value
ret_values = []
for value in values:
# Handle the submitted form value
if value in choices:
label = safe_text(choices.get(value))
# Should be returned as repr
if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR:
value = label
# Should be returned as mix
else:
value = "{0} ({1})".format(label, value)
ret_values.append(value)
# Overwrite ``cleaned_data`` of the ``form`` with object
# qualifier.
form.cleaned_data[self.data.name] = ret_values
# It's critically important to return the ``form`` with updated
# ``cleaned_data``
return form
form_element_plugin_registry.register(SelectMultipleWithMaxInputPlugin)

View file

@ -0,0 +1,85 @@
__title__ = 'fobi.contrib.plugins.form_elements.fields.select_multiple.forms'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2015 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('SelectMultipleInputForm',)
from django import forms
from django.utils.translation import ugettext_lazy as _
from fobi.base import BaseFormFieldPluginForm, get_theme
from fobi.helpers import validate_initial_for_multiple_choices
theme = get_theme(request=None, as_instance=True)
class SelectMultipleWithMaxInputForm(forms.Form, BaseFormFieldPluginForm):
"""
Form for ``SelectMultipleInputPlugin``.
"""
plugin_data_fields = [
("label", ""),
("name", ""),
("choices", ""),
("help_text", ""),
("initial", ""),
("required", False),
("max_choices", "")
]
label = forms.CharField(
label = _("Label"),
required = True,
widget = forms.widgets.TextInput(attrs={'class': theme.form_element_html_class})
)
name = forms.CharField(
label = _("Name"),
required = True,
widget = forms.widgets.TextInput(attrs={'class': theme.form_element_html_class})
)
choices = forms.CharField(
label = _("Choices"),
required = False,
help_text = _("Enter single values/pairs per line. Example:<code><br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;1<br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;2<br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;alpha, Alpha<br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;beta, Beta<br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;omega"
"</code><br/>"
"It finally transforms into the following HTML code:<code><br/>"
'&nbsp;&nbsp;&nbsp;&nbsp;&lt;select id="id_NAME_OF_THE_ELEMENT" name="NAME_OF_THE_ELEMENT"&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="1"&gt;1&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="2"&gt;2&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="alpha"&gt;Alpha&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="beta"&gt;Beta&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="omega"&gt;omega&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&lt;/select&gt;'
"</code>"),
widget = forms.widgets.Textarea(attrs={'class': theme.form_element_html_class})
)
help_text = forms.CharField(
label = _("Help text"),
required = False,
widget = forms.widgets.Textarea(attrs={'class': theme.form_element_html_class})
)
initial = forms.CharField(
label = _("Initial"),
required = False,
widget = forms.widgets.TextInput(attrs={'class': theme.form_element_html_class})
)
required = forms.BooleanField(
label = _("Required"),
required = False,
widget = forms.widgets.CheckboxInput(attrs={'class': theme.form_element_checkbox_html_class})
)
max_choices = forms.IntegerField(
label = _("Max choices"),
required = False,
widget=forms.widgets.NumberInput(attrs={'class': theme.form_element_html_class})
)
def clean_initial(self):
"""
Validating the initial value.
"""
return validate_initial_for_multiple_choices(self, 'choices', 'initial')

View file

@ -0,0 +1,13 @@
__title__ = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max.settings'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = 'Copyright (c) 2014-2015 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('SUBMIT_VALUE_AS',)
from fobi.helpers import validate_submit_value_as
from .conf import get_setting
SUBMIT_VALUE_AS = get_setting('SUBMIT_VALUE_AS')
validate_submit_value_as(SUBMIT_VALUE_AS)