def _get_fields(self): if self._fields is None: self._fields = fields = {} model = self._model non_hiddable_fnames = self._non_hiddable_fnames fconfigs = FieldsConfig.LocalCache() is_field_hidden = fconfigs.get_4_model(model).is_field_hidden excluded = self.excluded_fields # TODO: use meta.ModelFieldEnumerator (need to be improved for grouped options) for field in model._meta.fields: if field.get_tag('viewable') and not is_date_field(field) \ and not isinstance(field, excluded): if isinstance(field, ModelForeignKey): self._build_related_fields(field, fields, fconfigs) elif not is_field_hidden( field) or field.name in non_hiddable_fnames: fields[field.name] = [field] for field in model._meta.many_to_many: # if field.get_tag('viewable'): if field.get_tag(FieldTag.VIEWABLE): # TODO: test not viewable self._build_related_fields(field, fields, fconfigs) return self._fields
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) model = self.graph.model self._field: Optional['Field'] = None self.verbose_name = '??' field_name = self.value if not field_name: logger.warning('Instance block: no given field in block config.') self.error = _('No field given.') else: try: field = model._meta.get_field(field_name) except FieldDoesNotExist as e: logger.warning( 'Instance block: invalid field in block config: %s', e, ) self.error = _('The field is invalid.') else: fconf = FieldsConfig.LocalCache() error = self._check_field(field=field, fields_configs=fconf) if error: logger.warning( 'Instance block: invalid field in block config: %s', error, ) self.error = error else: self.verbose_name = gettext('{field} (Field)').format( field=field.verbose_name, ) self._field = field
def test_localcache_get_4_models(self): model1 = FakeContact model2 = FakeOrganisation h_field1 = 'phone' h_field2 = 'url_site' create_fc = FieldsConfig.create create_fc(model1, descriptions=[(h_field1, { FieldsConfig.HIDDEN: True })]) create_fc(model2, descriptions=[(h_field2, { FieldsConfig.HIDDEN: True })]) lc = FieldsConfig.LocalCache() fconfigs = lc.get_4_models([model1, model2]) fc1 = fconfigs.get(model1) self.assertIsInstance(fc1, FieldsConfig) self.assertEqual(model1, fc1.content_type.model_class()) self.assertTrue(fc1.is_fieldname_hidden(h_field1)) self.assertTrue(fconfigs.get(model2).is_fieldname_hidden(h_field2)) with self.assertNumQueries(0): lc.get_4_model(model1)
def __init__(self, instance_to_delete, fk_handler_classes=None, *args, **kwargs): super().__init__(*args, **kwargs) self.instance_to_delete = instance_to_delete self.fields_configs = FieldsConfig.LocalCache() self.fk_handler_classes = fk_handler_classes or self.fk_handler_classes self.handlers = self._build_handlers() self._add_handlers_fields()
def choices(cls, model): fconf = FieldsConfig.LocalCache() check_field = partial(cls._check_field, fields_configs=fconf) yield from ModelFieldEnumerator( # model, deep=0, only_leafs=False, model, depth=0, only_leaves=False, ).filter( # lambda f, deep: check_field(field=f) is None, lambda model, field, depth: check_field(field=field) is None, ).choices()
def test_localcache_is_fieldinfo_hidden05(self): "Field in CremeEntity." hidden = 'description' FieldsConfig.objects.create( content_type=FakeImage, descriptions=[(hidden, {FieldsConfig.HIDDEN: True})], ) is_hidden = FieldsConfig.LocalCache().is_fieldinfo_hidden self.assertTrue(is_hidden(FieldInfo(FakeImage, hidden))) self.assertFalse(is_hidden(FieldInfo(FakeContact, 'image'))) self.assertTrue(is_hidden(FieldInfo(FakeContact, f'image__{hidden}')))
def test_localcache_is_fieldinfo_hidden06(self): "Sub-field with depth > 1." hidden = 'description' FieldsConfig.objects.create( content_type=FakeFolder, descriptions=[(hidden, {FieldsConfig.HIDDEN: True})], ) is_hidden = FieldsConfig.LocalCache().is_fieldinfo_hidden self.assertTrue(is_hidden(FieldInfo(FakeFolder, hidden))) self.assertFalse(is_hidden(FieldInfo(FakeDocument, hidden))) self.assertFalse(is_hidden(FieldInfo(FakeDocument, 'linked_folder'))) self.assertTrue(is_hidden(FieldInfo(FakeDocument, f'linked_folder__{hidden}'))) self.assertFalse(is_hidden(FieldInfo(FakeDocument, 'linked_folder__parent'))) self.assertTrue(is_hidden(FieldInfo(FakeDocument, f'linked_folder__parent__{hidden}')))
def test_localcache_is_fieldinfo_hidden04(self): "Sub-field hidden." hidden = 'exif_date' FieldsConfig.objects.create( content_type=FakeImage, descriptions=[ (hidden, {FieldsConfig.HIDDEN: True}), ], ) is_hidden = FieldsConfig.LocalCache().is_fieldinfo_hidden self.assertFalse(is_hidden(FieldInfo(FakeContact, 'first_name'))) self.assertFalse(is_hidden(FieldInfo(FakeContact, 'image'))) self.assertTrue(is_hidden(FieldInfo(FakeContact, f'image__{hidden}')))
def __init__(self, model): super().__init__(model=model) self.fields_configs = FieldsConfig.LocalCache()
def _fields_configs(self) -> FieldsConfig.LocalCache: "Protected API. Useful for ReportHands/ReportGraphHand (in order to avoid queries)" return FieldsConfig.LocalCache()
def test_localcache_is_fieldinfo_hidden01(self): "No field configured." is_hidden = FieldsConfig.LocalCache().is_fieldinfo_hidden self.assertFalse(is_hidden(FieldInfo(FakeContact, 'first_name'))) self.assertFalse(is_hidden(FieldInfo(FakeContact, 'image'))) self.assertFalse(is_hidden(FieldInfo(FakeContact, 'image__exif_date')))
def __init__(self, entity, *args, **kwargs): super().__init__(*args, **kwargs) self.report = entity report_ct = entity.ct model = report_ct.model_class() instance = self.instance fields = self.fields aggregate_field_f = fields['aggregate_field'] abscissa_field_f = fields['abscissa_field'] is_count_f = fields['is_count'] get_fconf = FieldsConfig.LocalCache().get_4_model # TODO: split('__', 1) when 'count' is an aggregate operator ordinate_field_name, __, aggregate = instance.ordinate.rpartition('__') # Abscissa ------------------------------------------------------------- def absc_field_excluder(field, deep): # TODO: set the ForeignKeys to entities as not enumerable automatically ? if isinstance(field, RelatedField) and \ issubclass(field.remote_field.model, CremeEntity): return True return get_fconf(field.model).is_field_hidden(field) and \ field.name != instance.abscissa abscissa_model_fields = ModelFieldEnumerator(model, deep=0, only_leafs=False) \ .filter(self._filter_abcissa_field, viewable=True) \ .exclude(absc_field_excluder) \ .choices() self.rtypes = rtypes = dict(RelationType.objects .compatible(report_ct, include_internals=True) .values_list('id', 'predicate') ) abscissa_predicates = [*rtypes.items()] sort_key = collator.sort_key abscissa_predicates.sort(key=lambda k: sort_key(k[1])) abscissa_choices = [ (_('Fields'), abscissa_model_fields), (_('Relationships'), abscissa_predicates), ] self.abs_cfields = cfields = { cf.id: cf for cf in CustomField.objects .filter(field_type__in=(CustomField.ENUM, CustomField.DATETIME, ), content_type=report_ct, ) } if cfields: # TODO: sort ? abscissa_choices.append( (_('Custom fields'), [(cf.id, cf.name) for cf in cfields.values()]) ) # TODO: we could build the complete map fields/allowed_types, instead of doing AJAX queries... abscissa_field_f.choices = abscissa_choices abscissa_field_f.widget.target_url = reverse('reports__graph_types', args=(report_ct.id,)) # Meh # Ordinate ------------------------------------------------------------- def agg_field_excluder(field, deep): return get_fconf(field.model).is_field_hidden(field) and \ field.name != ordinate_field_name aggfields = [ field_info[0] for field_info in ModelFieldEnumerator(model, deep=0) .filter((lambda f, depth: isinstance(f, field_aggregation_registry.authorized_fields)), viewable=True ) .exclude(agg_field_excluder) ] aggfield_choices = [(field.name, field.verbose_name) for field in aggfields] aggcustom_choices = [ *CustomField.objects .filter(field_type__in=field_aggregation_registry.authorized_customfields, content_type=report_ct, ) .values_list('id', 'name') ] ordinate_choices = aggfield_choices or aggcustom_choices if ordinate_choices: self.force_count = False money_fields = [field for field in aggfields if isinstance(field, MoneyField)] if money_fields: # TODO: lazy lazily-translated-string interpolation aggregate_field_f.help_text = gettext( 'If you use a field related to money, the entities should use the same ' 'currency or the result will be wrong. Concerned fields are : {}' ).format(', '.join(str(field.verbose_name) for field in money_fields)) if aggcustom_choices and aggfield_choices: ordinate_choices = [ (_('Fields'), aggfield_choices), (_('Custom fields'), aggcustom_choices), ] else: self.force_count = True ordinate_choices = [('', _('No field is usable for aggregation'))] disabled_attrs = {'disabled': True} aggregate_field_f.widget.attrs = disabled_attrs fields['aggregate'].widget.attrs = disabled_attrs is_count_f.help_text = _('You must make a count because no field is usable for aggregation') is_count_f.initial = True is_count_f.widget.attrs = disabled_attrs aggregate_field_f.choices = ordinate_choices # Initial data --------------------------------------------------------- data = self.data if data: get_data = data.get widget = abscissa_field_f.widget widget.source_val = get_data('abscissa_field') widget.target_val = get_data('abscissa_group_by') # NB: ugly hack ; see AbscissaGroupBySelect/toggleDaysField. # Should be removed in Creme2.2 with a clean field/widget fields['abscissa_group_by'].widget.attrs['data-initial-value'] = get_data('abscissa_group_by') elif instance.pk is not None: fields['aggregate'].initial = aggregate aggregate_field_f.initial = ordinate_field_name abscissa_field_f.initial = instance.abscissa widget = abscissa_field_f.widget widget.source_val = instance.abscissa widget.target_val = instance.type # NB: idem fields['abscissa_group_by'].widget.attrs['data-initial-value'] = instance.type # TODO: remove this sh*t when is_count is a real widget well initialized (disabling set by JS) if is_count_f.initial or instance.is_count or data.get('is_count'): disabled_attrs = {'disabled': True} aggregate_field_f.widget.attrs = disabled_attrs fields['aggregate'].widget.attrs = disabled_attrs