def __init__(self, to_register=DEFAULT_SORTERS): # self._sorters_4_modelfields = {} # TODO: when order is kept (py3.6+) self._sorters_4_modelfields = OrderedDict() self._sorters_4_modelfieldtypes = ClassKeyedMap(default=None) for model_field_cls, sorter_cls in to_register: self.register_model_field_type(type=model_field_cls, sorter_cls=sorter_cls)
def __init__(self, to_register=DEFAULT_REGISTRATIONS, choice_sfield_builder=lv_form.RegularChoiceField): self._builders_4_modelfields = {} self._builders_4_modelfieldtypes = ClassKeyedMap(default=None) self.register_choice_builder(choice_sfield_builder) for model_field_cls, builder in to_register: self.register_model_field_type(type=model_field_cls, sfield_builder=builder)
def __init__(self, models_to_register: Iterable[Tuple[ Type[Model], Type[AbstractCellSorter]]] = DEFAULT_MODELS): self._sorters: ClassKeyedMap = ClassKeyedMap(default=None) for model, sorter_cls in models_to_register: self.register(model=model, sorter_cls=sorter_cls)
def __init__(self, default=lv_form.RegularRelatedField, models_to_register=DEFAULT_MODELS): self._builders_4_models = ClassKeyedMap(default=None) self.register_default(default) for model, builder in models_to_register: self.register_related_model(model=model, sfield_builder=builder)
def test_main(self): class Klass1: pass class Klass2: pass class Klass3: pass ckm = ClassKeyedMap([(Klass1, 1), (Klass2, 2)]) self.assertEqual(1, ckm[Klass1]) self.assertEqual(2, ckm[Klass2]) self.assertEqual(2, len(ckm)) self.assertIsNone(ckm[Klass3]) self.assertEqual(3, len(ckm)) self.assertIsNone(ckm.default) self.assertTrue(hasattr(ckm, '__contains__')) self.assertIn(Klass1, ckm) empty = ClassKeyedMap() self.assertIs(True, bool(ckm)) self.assertIs(False, bool(empty)) self.assertEqual(0, len(empty)) self.assertNotIn(Klass1, empty) keys_set = {Klass1, Klass2, Klass3} self.assertEqual(keys_set, set(ckm)) self.assertEqual(keys_set, set(ckm.keys())) self.assertEqual({1, 2, None}, set(ckm.values())) self.assertEqual({(Klass1, 1), (Klass2, 2), (Klass3, None)}, set(ckm.items()) ) r = repr(ckm) self.assertTrue(r.startswith('ClassKeyedMap('))
def test_inheritage03(self): "Inheritance order must be kept when cache is filled too" class Klass1: pass class Klass2(Klass1): pass class Klass3(Klass2): pass class Klass4(Klass3): pass ckm = ClassKeyedMap([(Klass1, 1), (Klass3, 3)], # Not 2 & 4 default=0 ) self.assertEqual(1, ckm[Klass2]) self.assertEqual(3, ckm[Klass4])
def __init__(self, to_register: Iterable[ Tuple[Type[Field], Type[AbstractCellSorter]]] = DEFAULT_SORTERS): # self._sorters_4_modelfields = {} # TODO: when order is kept (py3.7) self._sorters_4_modelfields: Dict[Field, AbstractCellSorter] = OrderedDict() self._sorters_4_modelfieldtypes: ClassKeyedMap = ClassKeyedMap( default=None) for model_field_cls, sorter_cls in to_register: self.register_model_field_type(type=model_field_cls, sorter_cls=sorter_cls)
def test_inheritage02(self): "Inheriting values: more complex case (the nearest parent should be found)" class Klass1: pass class Klass2(Klass1): pass class Klass3(Klass2): pass class Klass4(Klass3): pass ckm = ClassKeyedMap([(Klass1, 1), (Klass2, 2), (Klass3, 3), (Klass4, 4)], default=0 ) class Klass5(Klass4): pass self.assertEqual(4, ckm[Klass5])
def test_inheritage01(self): "Inheriting values" class Klass1: pass class Klass2: pass class Klass3(Klass2): pass class Klass4(Klass3): pass ckm = ClassKeyedMap([(Klass1, 1), (Klass2, 2)]) self.assertEqual(2, ckm[Klass3]) self.assertEqual(2, ckm[Klass4]) ckm[Klass3] = 3 self.assertEqual(3, ckm[Klass3]) self.assertEqual(3, ckm[Klass4]) # Cache must be updated
def test_setitem(self): "Other default value + __setitem__" class Klass1: pass class Klass2: pass class Klass3: pass ckm = ClassKeyedMap(default=0) result = ckm[Klass1] = 1 ckm[Klass2] = 2 self.assertEqual(0, ckm[Klass3]) self.assertEqual(1, ckm[Klass1]) self.assertEqual(1, result) self.assertEqual(3, len(ckm)) self.assertEqual(0, ckm[Klass3]) # 2nd access should hit the cache self.assertEqual(3, len(ckm))
class RegularFieldSearchRegistry(AbstractListViewSearchFieldRegistry): """Class of ListViewSearchFieldRegistry specialized for cells representing model fields (CharField, BooleanField, ForeignKey...). The returned search-field can be customised depending on (from greater priority to lesser): - A model-field (eg: the field "name" of your model <Book>). - The class of the model-field (eg: fields which have class inheriting CharField). There is a special case for model-fields which have choices. """ DEFAULT_REGISTRATIONS = ( (models.CharField, lv_form.RegularCharField), (models.TextField, lv_form.RegularCharField), (models.IntegerField, lv_form.RegularIntegerField), (models.PositiveIntegerField, lv_form.RegularPositiveIntegerField), (models.PositiveSmallIntegerField, lv_form.RegularPositiveIntegerField), (models.FloatField, lv_form.RegularFloatField), (models.DecimalField, lv_form.RegularDecimalField), (models.BooleanField, lv_form.RegularBooleanField), # (models.NullBooleanField, RegularBooleanField), (models.DateField, lv_form.RegularDateField), # (models.TimeField, ), TODO (models.ForeignKey, RegularRelatedFieldSearchRegistry), (models.ManyToManyField, RegularRelatedFieldSearchRegistry), # (models.OneToOneField, RegularRelatedFieldSearchRegistry), TODO # (models.IPAddressField, ...) # TODO: what about ? # TODO: needs JSONField management in the RDBMS... # (fields.DurationField, ), # (fields.DatePeriodField, ), # No search # (models.FileField, ), # (models.ImageField, ), ) def __init__(self, to_register=DEFAULT_REGISTRATIONS, choice_sfield_builder=lv_form.RegularChoiceField): self._builders_4_modelfields = {} self._builders_4_modelfieldtypes = ClassKeyedMap(default=None) self.register_choice_builder(choice_sfield_builder) for model_field_cls, builder in to_register: self.register_model_field_type(type=model_field_cls, sfield_builder=builder) def builder_4_model_field(self, *, model, field_name): field = model._meta.get_field(field_name) return self._builders_4_modelfields.get(field) def builder_4_model_field_type(self, model_field): return self._builders_4_modelfieldtypes[model_field] @property def choice_builder(self): return self._choice_builder def get_field(self, *, cell, user, **kwargs): model_field = cell.field_info[-1] return self._build_field( builder=self._builders_4_modelfields.get(model_field) or (self._choice_builder if model_field.choices else self._builders_4_modelfieldtypes[type(model_field)]), cell=cell, user=user, **kwargs) def pretty(self, indent=0): indent_str = ' ' * indent res = '{indent}{name}:\n{indent} Choice:\n{choice}\n{indent} Field types:'.format( indent=indent_str, name=type(self).__name__, choice=self._pretty_builder(self._choice_builder, indent=indent + 4), ) for field_type, builder in self._builders_4_modelfieldtypes.items(): res += '\n{} [{}.{}]:\n{}'.format( indent_str, field_type.__module__, field_type.__name__, self._pretty_builder(builder, indent=indent + 6), ) res += '\n{} Fields:'.format(indent_str) modelfields = self._builders_4_modelfields if modelfields: for field, builder in modelfields.items(): res += '\n{} [{}]:\n{}'.format( indent_str, field, self._pretty_builder(builder, indent=indent + 6), ) else: res += '\n{} (empty)'.format(indent_str) return res def register_choice_builder(self, sfield_builder): self._choice_builder = self._instantiate_builder(sfield_builder) return self def register_model_field(self, *, model, field_name, sfield_builder): field = model._meta.get_field(field_name) self._builders_4_modelfields[field] = self._instantiate_builder( sfield_builder) # TODO ? # if self._enums_4_fields.setdefault(field, enumerator_class) is not enumerator_class: # raise self.RegistrationError( # '_EnumerableRegistry: this field is already registered: {model}.{field}'.format( # model=model.__name__, field=field_name, # ) # ) return self def register_model_field_type(self, *, type, sfield_builder): self._builders_4_modelfieldtypes[type] = self._instantiate_builder( sfield_builder) return self
class RegularFieldSorterRegistry(AbstractCellSorter): """Class of sorter for all types of EntityCellRegularField. Sub-sorters can be registered to customise the behaviour for specific model-fields & model-field classes. """ DEFAULT_SORTERS = ( (models.AutoField, RegularFieldSorter), (models.BooleanField, RegularFieldSorter), (models.DecimalField, RegularFieldSorter), (models.FloatField, RegularFieldSorter), (models.IntegerField, RegularFieldSorter), (models.CharField, RegularFieldSorter), (models.TextField, RegularFieldSorter), (models.DateField, RegularFieldSorter), (models.TimeField, RegularFieldSorter), (models.ForeignKey, ForeignKeySorterRegistry), # No sorting # models.ManyToManyField # models.OneToOneField (models.CommaSeparatedIntegerField, VoidSorter), # models.FilePathField # models.BinaryField # models.UUIDField # (fields.DurationField, VoidSorter), TODO ? (fields.DatePeriodField, VoidSorter ), # TODO: needs JSONField management in the RDBMS... # TODO: what about ? # (models.DurationField, ...) # (models.IPAddressField, ...) # (models.GenericIPAddressField, ...) # (models.SlugField, ...) # (models.URLField, ...) ) def __init__(self, to_register=DEFAULT_SORTERS): # self._sorters_4_modelfields = {} # TODO: when order is kept (py3.6+) self._sorters_4_modelfields = OrderedDict() self._sorters_4_modelfieldtypes = ClassKeyedMap(default=None) for model_field_cls, sorter_cls in to_register: self.register_model_field_type(type=model_field_cls, sorter_cls=sorter_cls) def get_field_name(self, cell): field = cell.field_info[-1] sorter = self._sorters_4_modelfields.get(field) or \ self._sorters_4_modelfieldtypes[type(field)] return None if sorter is None else sorter.get_field_name(cell=cell) def pretty(self, indent=0): indent_str = ' ' * indent res = '{indent}{name}:\n{indent} Field types:'.format( indent=indent_str, name=type(self).__name__, ) for field_type, sorter in self._sorters_4_modelfieldtypes.items(): res += '\n{} [{}.{}]:\n{}'.format( indent_str, field_type.__module__, field_type.__name__, sorter.pretty(indent=indent + 6), ) res += '\n{} Fields:'.format(indent_str) modelfields = self._sorters_4_modelfields if modelfields: for field, sorter in modelfields.items(): res += '\n{} [{}]:\n{}'.format( indent_str, field, sorter.pretty(indent=indent + 6), ) else: res += '\n{} (empty)'.format(indent_str) return res def register_model_field(self, *, model, field_name, sorter_cls): field = model._meta.get_field(field_name) self._sorters_4_modelfields[field] = sorter_cls() # TODO ? # if self._enums_4_fields.setdefault(field, enumerator_class) is not enumerator_class: # raise self.RegistrationError( # '_EnumerableRegistry: this field is already registered: {model}.{field}'.format( # model=model.__name__, field=field_name, # ) # ) return self def register_model_field_type(self, *, type, sorter_cls): self._sorters_4_modelfieldtypes[type] = sorter_cls() return self def sorter_4_model_field(self, *, model, field_name): field = model._meta.get_field(field_name) return self._sorters_4_modelfields.get(field) def sorter_4_model_field_type(self, model_field): return self._sorters_4_modelfieldtypes[model_field]
def __init__(self, models_to_register=DEFAULT_MODELS): self._sorters = ClassKeyedMap(default=None) for model, sorter_cls in models_to_register: self.register(model=model, sorter_cls=sorter_cls)
def __init__(self, default_field_explainer_class=FieldChangeExplainer): self._line_explainer_classes = {} self._field_explainer_classes = ClassKeyedMap( default=default_field_explainer_class)