from django import forms from django.db.models import Q # TODO: import particular classes? import crispy_forms.bootstrap as bootstrap import crispy_forms.layout as layout from .query_managers import ( SingleValueQueryManager, MultiValueQueryManager, RangesQueryManager, SingleRegexQueryManager, RegexQueryManager, ComboQueryManager ) bootstrap.InlineCheckboxes.template = 'checkboxselectmultiple_inline_with_tooltips.html' class LayoutField(object): def layout(self, x, **kwargs): return layout.Field(x, **kwargs) class CheckboxesLayoutField(object): def layout(self, x, **kwargs): if self.checkboxes: return bootstrap.InlineCheckboxes(x, **kwargs) else: return layout.Field(x, **kwargs) class RadiosLayoutField(object): def layout(self, x, **kwargs): return bootstrap.InlineRadios(x, **kwargs) class SelectLayoutField(object): def layout(self, x, **kwargs): return layout.Field(x, css_class='custom-select', **kwargs) # ============================================================================== #TODO move implementation common for RangesFilter and RegexFilter to an abstract ExpressionFilter? class RangeFilter(forms.CharField, LayoutField): def __init__(self, label, lookup, initial='', empty_value='', **kwargs): super().__init__(label=label, required=False, initial=initial, empty_value=empty_value, **kwargs) self.query_manager = RangesQueryManager(lookup) # can’t use static default_validators since the query manager is a class field def validate(self, value): self.query_manager.expression_validator(value) class RegexFilter(forms.CharField, LayoutField): def __init__(self, label, lookup, additional_operators=False, initial='.*', empty_value='.*', autocomplete=None, **kwargs): self.css_class = 'regex-autocomplete' if autocomplete else None self.data_autocomplete = autocomplete if autocomplete else None super().__init__(label=label, required=False, initial=initial, empty_value=empty_value, **kwargs) self.query_manager = RegexQueryManager(lookup, additional_operators=additional_operators) # can’t use static default_validators since validation depends on the # query_manager instance (allowed operators) def validate(self, value): self.query_manager.expression_validator(value) def layout(self, x, **kwargs): return layout.Field(x, css_class=self.css_class, data_autocomplete=self.data_autocomplete, **kwargs) class SingleRegexFilter(forms.CharField, LayoutField): def __init__(self, label, lookup, initial='.*', empty_value='.*', **kwargs): super().__init__(label=label, required=False, initial=initial, empty_value=empty_value, **kwargs) self.query_manager = SingleRegexQueryManager(lookup) class MultipleChoiceFilter(forms.MultipleChoiceField, CheckboxesLayoutField): def __init__(self, label, choices, lookup, **kwargs): super().__init__( label=label, choices=choices, required=False, **kwargs ) self.query_manager = MultiValueQueryManager(lookup, default_conjunction=False) self.checkboxes = True def valid_value(self, value): text_value = str(value) # ignore the optional help tooltip provided in choices, only compare with the key for k, (v, help) in self.choices: print(k, value) if value == k or text_value == str(k): return True return False class ModelMultipleChoiceFilter(forms.ModelMultipleChoiceField, CheckboxesLayoutField): def __init__(self, label, queryset, key, lookup, human_values={}, checkboxes=True, **kwargs): super().__init__( label=label, queryset=queryset, required=False, **kwargs ) self.query_manager = MultiValueQueryManager(lookup, default_conjunction=False) self.key = key self.human_values = human_values self.checkboxes = checkboxes def label_from_instance(self, obj): val = self.human_values.get(obj.__dict__[self.key], obj.__dict__[self.key]) if self.checkboxes: if type(val) == tuple: return val else: return (val, None) else: if type(val) == tuple: return '{} ({})'.format(val[1], val[0]) else: return val class ChoiceFilter(forms.ChoiceField, SelectLayoutField): def __init__(self, label, choices, **kwargs): super().__init__( label=label, choices=choices, required=False, **kwargs ) class ModelChoiceFilter(forms.ModelChoiceField, SelectLayoutField): def __init__(self, label, queryset, key, lookup, human_values={}, **kwargs): super().__init__( label=label, queryset=queryset, required=False, **kwargs ) self.query_manager = SingleValueQueryManager(lookup) # str object or callable self.key = key self.human_values = human_values def label_from_instance(self, obj): if type(self.key) == str: return self.human_values.get(obj.__dict__[self.key], obj.__dict__[self.key]) else: return self.key(obj) class OperatorField(forms.ChoiceField, RadiosLayoutField): def __init__(self): super().__init__( label=' ', choices=((False, 'lub'), (True, 'i')), initial=False, required=False, ) def to_python(self, value): if value in ('1', 'true', 'True', True): return True if value in ('0', 'false', 'False', False): return False return None class SwitchField(forms.BooleanField, LayoutField): def __init__(self, label, css_class=None): super().__init__(label=label, required=False) self.css_class = css_class def layout(self, x, **kwargs): wrapper_class = 'custom-switch float-right font-weight-bold' if self.css_class: wrapper_class += ' ' + self.css_class return layout.Field(x, wrapper_class=wrapper_class, **kwargs)