def many_to_many(cls, call_target, model_field, **kwargs): setdefaults_path( kwargs, choices=model_field.remote_field.model.objects.all(), extra__django_related_field=True, ) return call_target(model_field=model_field, **kwargs)
def create_declared_member(model_field_name): definition_or_member = member_params_by_member_name.pop(model_field_name, {}) name = model_field_name.replace('__', '_') if isinstance(definition_or_member, dict): definition = setdefaults_path( Namespace(), definition_or_member, _name=name, # TODO: this should work, but there's a bug in tri.declarative, working around for now # call_target__attribute='from_model' if definition_or_member.get('attr', model_field_name) is not None else None, call_target__cls=member_class, ) if definition_or_member.get('attr', model_field_name) is not None: setdefaults_path( definition, call_target__attribute='from_model', ) if include is not None and name in include: setdefaults_path( definition, include=True, ) member = definition( model=model, model_field_name=definition_or_member.get('attr', model_field_name), ) else: member = definition_or_member if member is None: return members[name] = member
def foreign_key(cls, model_field, model, call_target, **kwargs): del model setdefaults_path( kwargs, choices=model_field.foreign_related_fields[0].model.objects.all(), ) return call_target(model_field=model_field, **kwargs)
def __html__(self, *, render=None): a = setdefaults_path( Namespace(), self.a, children__text=self.display_name, attrs__href=self.url, _name='a', ) if self._active: setdefaults_path( a, attrs__class={self.active_class: True}, ) if self.url is None and a.tag == 'a': a.tag = None fragment = Fragment( children__a=a, tag=self.tag, template=self.template, attrs=self.attrs, _name='fragment', ) fragment = fragment.bind(parent=self) # need to do this here because otherwise the sub menu will get get double bind for name, item in items(self.sub_menu): assert name not in fragment.children fragment.children[name] = item return fragment.__html__()
def multi_choice(cls, call_target=None, **kwargs): """ Field that has one value out of a set. :type choices: list """ setdefaults_path(kwargs, dict(field__choices=kwargs.get('choices'), )) return call_target(**kwargs)
def __init__(self, *bases, base_template=None, content_block=None, assets=None, root=None, internal=False, **kwargs): self.name = None self.internal = internal self.base_template = base_template if not self.base_template: for base in reversed(bases): if base.base_template: self.base_template = base.base_template break self.content_block = content_block if not self.content_block: for base in reversed(bases): if base.content_block: self.content_block = base.content_block break if assets: from iommi.debug import iommi_debug_on if iommi_debug_on(): print("Warning: The preferred way to add top level assets config to a Style is via the root argument. " "I.e. assets__* becomes root__assets__*") setdefaults_path(root, assets=assets) self.root = {k: v for k, v in items(Namespace(*(base.root for base in bases), root)) if v is not None} self.config = Namespace(*[x.config for x in bases], recursive_namespace(kwargs))
def __init__(self, text=None, *, children: Optional[Dict[str, PartType]] = None, **kwargs): super(Fragment, self).__init__(**kwargs) if text is not None: setdefaults_path( children, text=text, ) collect_members(self, name='children', items=children, cls=Fragment, unknown_types_fall_through=True)
def choice(cls, call_target=None, **kwargs): """ Field that has one value out of a set. :type choices: list """ assert 'choices' in kwargs, 'To use Filter.choice, you must pass the choices list' setdefaults_path(kwargs, dict(field__choices=kwargs.get('choices'), )) return call_target(**kwargs)
def test_setdefatults_path_retain_empty(): assert setdefaults_path(Namespace(a=Namespace()), a__b=Namespace()) == Namespace(a__b=Namespace(), ) assert setdefaults_path( Namespace(), attrs__class=Namespace(), ) == Namespace(attrs__class=Namespace(), )
def test_setdefatults_path_retain_empty(): actual = setdefaults_path(Namespace(a=Namespace()), a__b=Namespace()) expected = Namespace(a__b=Namespace()) assert expected == actual actual = setdefaults_path(Namespace(), attrs__class=Namespace()) expected = Namespace(attrs__class=Namespace()) assert expected == actual
def member_from_model(cls, model, factory_lookup, defaults_factory, factory_lookup_register_function=None, model_field_name=None, model_field=None, **kwargs): if model_field is None: assert model_field_name is not None, "Field can't be automatically created from model, you must specify it manually" sub_field_name, _, field_path_rest = model_field_name.partition('__') # noinspection PyProtectedMember model_field = get_field(model, sub_field_name) if field_path_rest: result = member_from_model( cls=cls, model=model_field.remote_field.model, factory_lookup=factory_lookup, defaults_factory=defaults_factory, factory_lookup_register_function=factory_lookup_register_function, model_field_name=field_path_rest, **kwargs) set_and_remember_for_reinvoke(result, attr=model_field_name) return result factory = factory_lookup.get(type(model_field), MISSING) if factory is MISSING: for django_field_type, foo in reversed(list(factory_lookup.items())): if isinstance(model_field, django_field_type): factory = foo break # pragma: no mutate optimization if factory is MISSING: message = f'No factory for {type(model_field).__name__}.' if factory_lookup_register_function is not None: message += ' Register a factory with register_factory or %s, you can also register one that returns None to not handle this field type' % factory_lookup_register_function.__name__ raise AssertionError(message) if factory is None: return None # Not strict evaluate on purpose factory = evaluate(factory, __match_empty=False, model_field=model_field, model_field_name=model_field_name) setdefaults_path( kwargs, _name=model_field_name, call_target__cls=cls, ) defaults = defaults_factory(model_field) if isinstance(factory, Namespace): factory = setdefaults_path( Namespace(), factory, defaults, ) else: kwargs.update(**defaults) return factory(model_field=model_field, model_field_name=model_field_name, model=model, **kwargs)
def info(cls, value, call_target=None, **kwargs): """ Shortcut to create an info entry. """ setdefaults_path( kwargs, initial=value, ) return call_target(**kwargs)
def many_to_many(cls, call_target, model_field, **kwargs): setdefaults_path( kwargs, choices=model_field.remote_field.model.objects.all(), read_from_instance=many_to_many_factory_read_from_instance, write_to_instance=many_to_many_factory_write_to_instance, extra__django_related_field=True, ) return call_target(model_field=model_field, **kwargs)
def icon(cls, icon, *, display_name=None, call_target=None, icon_classes=None, **kwargs): icon_classes_str = ' '.join(['fa-' + icon_class for icon_class in icon_classes]) if icon_classes else '' if icon_classes_str: icon_classes_str = ' ' + icon_classes_str setdefaults_path( kwargs, display_name=format_html('<i class="fa fa-{}{}"></i> {}', icon, icon_classes_str, display_name), ) return call_target(**kwargs)
def __html__(self, *, render=None): setdefaults_path( render, template=self.template, context=self.iommi_evaluate_parameters().copy(), ) request = self.get_request() render.context.update(csrf(request)) return render(request=request)
def __html__(self, *, render=None): if not self.iommi_bound_members().filters._bound_members: return '' setdefaults_path( render, context=self.iommi_evaluate_parameters(), template=self.template, ) return render(request=self.get_request())
def choice(cls, call_target=None, **kwargs): """ Shortcut for single choice field. If required is false it will automatically add an option first with the value '' and the title '---'. To override that text pass in the parameter empty_label. :param choice_to_option: callable with three arguments: form, field, choice. Convert from a choice object to a tuple of (choice, value, label, selected), the last three for the <option> element """ assert 'choices' in kwargs setdefaults_path( kwargs, empty_choice_tuple=(None, '', kwargs['empty_label'], True), ) return call_target(**kwargs)
def __html__(self, *, context=None, render=None): # TODO: what if self.template is a Template? setdefaults_path( render, context=context, template_name=self.template, ) request = self.request() render.context.update(csrf(request)) render.context['form'] = self return render(request=request)
def choice_queryset(cls, choices: QuerySet, call_target=None, **kwargs): """ Field that has one value out of a set. """ if 'model' not in kwargs: assert isinstance(choices, QuerySet), 'The convenience feature to automatically get the parameter model set only works for QuerySet instances' kwargs['model'] = choices.model setdefaults_path(kwargs, dict( field__choices=choices, field__model=kwargs['model'], choices=choices, )) return call_target(**kwargs)
def choice_queryset(cls, choices, call_target=None, **kwargs): if 'model' not in kwargs: if isinstance(choices, QuerySet): kwargs['model'] = choices.model elif 'model_field' in kwargs: kwargs['model'] = kwargs['model_field'].remote_field.model else: assert False, 'The convenience feature to automatically get the parameter model set only works for QuerySet instances or if you specify model_field' setdefaults_path( kwargs, choices=(lambda form, **_: choices.all()) if isinstance(choices, QuerySet) else choices, # clone the QuerySet if needed ) return call_target(**kwargs)
def test_setdefaults_callable_backward_not_namespace(): actual = setdefaults_path( Namespace(foo__x=17), foo=EMPTY, ) expected = Namespace(foo__x=17) assert actual == expected
def crud(cls, request, operation, form, app_name, model_name, pk=None, call_target=None, **kwargs): model = django_apps.all_models[app_name][model_name] instance = model.objects.get(pk=pk) if pk is not None else None if not cls.has_permission(request, operation=operation, model=model, instance=instance): raise Http404() def on_save(form, instance, **_): message = f'{form.model._meta.verbose_name.capitalize()} {instance} was ' + ('created' if form.extra.is_create else 'updated') messages.add_message(request, messages.INFO, message, fail_silently=True) def on_delete(form, instance, **_): message = f'{form.model._meta.verbose_name.capitalize()} {instance} was deleted' messages.add_message(request, messages.INFO, message, fail_silently=True) form = setdefaults_path( Namespace(), form, call_target__cls=cls.get_meta().form_class, auto__instance=instance, auto__model=model, call_target__attribute=operation, extra__on_save=on_save, extra__on_delete=on_delete, ) return call_target( **{f'parts__{operation}_{app_name}_{model_name}': form}, **kwargs, )
def collect_members(*, items_dict: Dict = None, items: Dict[str, Any] = None, cls: Type, unapplied_config: Dict) -> Dict[str, Any]: unbound_items = {} if items_dict is not None: for name, x in items_dict.items(): x.name = name unbound_items[name] = x if items is not None: for name, item in items.items(): if not isinstance(item, dict): item.name = name unbound_items[name] = item else: if name in unbound_items: unapplied_config[name] = item else: item = setdefaults_path( Namespace(), item, call_target__cls=cls, name=name, ) unbound_items[name] = item() return Struct({x.name: x for x in sort_after(list(unbound_items.values()))})
def list_model(model, app, table): app_name, model_name = app_and_name_by_model[model] kwargs = setdefaults_path( Namespace(), app.get(app_name, {}).get(model_name, {}), table=table, table__rows=model.objects.all(), table__extra_columns=dict( # TODO: bulk edit and bulk delete # select=dict(call_target__attribute='select', after=0), edit=dict(call_target__attribute='edit', after=0, cell__url=lambda row, **_: '%s/edit/' % row.pk), delete=dict(call_target__attribute='delete', after=LAST, cell__url=lambda row, **_: '%s/delete/' % row.pk), ), table__actions=dict( # TODO: bulk delete # bulk_delete=dict(call_target__attribute='submit', display_name='Delete', on_post=lambda table, **_: table.bulk_queryset().delete()), create=dict( display_name=f'Create {model._meta.verbose_name}', attrs__href='create/', ), ), table__call_target__attribute='from_model', table__query_from_indexes=True, ) return kwargs.table().as_page(parts__header=admin_h1)
def all_models(cls, request, table, call_target=None, **kwargs): if not cls.has_permission(request, operation='all_models'): raise Http404() def preprocess_rows(admin, rows, **_): return [ row for row in rows if admin.apps.get(f'{row.app_name}_{row.model_name}', {}).get('include', True) ] table = setdefaults_path( Namespace(), table, title='All models', call_target__cls=cls.get_meta().table_class, sortable=False, rows=[ Struct(app_name=app_name, model_name=model_name, model=model) for (app_name, model_name), model in items(model_by_app_and_name) ], preprocess_rows=preprocess_rows, columns=dict( app_name__auto_rowspan=True, app_name__after=0, model_name__cell__url=lambda row, **_: '%s/%s/' % (row.app_name, row.model_name), ), ) return call_target( parts__all_models=table, **kwargs )
def on_bind(self) -> None: bind_members(self, name='filters') self.advanced_simple_toggle = self.advanced_simple_toggle.bind(parent=self) request = self.get_request() self.query_advanced_value = request_data(request).get(self.get_advanced_query_param(), '') if request else '' # TODO: should it be possible to have freetext as a callable? this code just treats callables as truthy if any(f.freetext for f in values(declared_members(self)['filters'])): declared_members(self.form).fields[FREETEXT_SEARCH_NAME].include = True declared_fields = declared_members(self.form)['fields'] for name, filter in items(self.filters): assert filter.attr or not getattr(filter.value_to_q, 'iommi_needs_attr', False), f"{name} cannot be a part of a query, it has no attr or value_to_q so we don't know what to search for" if name in declared_fields: field = setdefaults_path( Namespace(), _name=name, attr=name if filter.attr is MISSING else filter.attr, model_field=filter.model_field, ) declared_fields[name] = declared_fields[name].reinvoke(field) set_declared_member(self.form, 'fields', declared_fields) for name, field in items(declared_fields): if name == FREETEXT_SEARCH_NAME: continue if name not in self.filters: field.include = False bind_members(self, name='endpoints') self.form = self.form.bind(parent=self) self._bound_members.form = self.form
def test_no_call_target_overwrite(): def f(): pass def b(): pass x = setdefaults_path( dict(foo={}), foo=f, ) assert x == dict(foo=dict(call_target=f)) y = setdefaults_path( x, foo=b, ) assert dict(foo=dict(call_target=f)) == y
def as_create_or_edit_page(cls, *, call_target=None, extra=None, model=None, instance=None, on_save=None, redirect=None, redirect_to=None, parts=None, name, title=None, **kwargs): assert 'request' not in kwargs, "I'm afraid you can't do that Dave" if model is None and instance is not None: model = type(instance) if title is None: title = '%s %s' % ('Create' if extra.is_create else 'Save', model._meta.verbose_name.replace('_', ' ')) extra.on_save = on_save extra.redirect = redirect extra.redirect_to = redirect_to setdefaults_path( kwargs, actions__submit=dict( attrs__value=title, attrs__name=name, ), ) from iommi.page import Page from iommi.page import html return Page( parts={ # TODO: do we really need to pop from parts ourselves here? 'title': html.h1(title, **parts.pop('title', {})), name: call_target( extra=extra, model=model, instance=instance, **kwargs), **parts })
def test_setdefaults_path_empty_marker_no_side_effect(): assert setdefaults_path( Namespace(a__b=1, a__c=2), a=Namespace(d=3), a__e=4, ) == Namespace( a__b=1, a__c=2, a__d=3, a__e=4, )
def collect_members(container, *, name: str, items_dict: Dict = None, items: Dict[str, Any] = None, cls: Type, unknown_types_fall_through=False): forbidden_names = FORBIDDEN_NAMES & (set(keys(items_dict or {})) | set(keys(items or {}))) if forbidden_names: raise ForbiddenNamesException(f'The names {", ".join(sorted(forbidden_names))} are reserved by iommi, please pick other names') assert name != 'items' unbound_items = Struct() _unapplied_config = {} if items_dict is not None: for key, x in items_of(items_dict): x._name = key unbound_items[key] = x if items is not None: for key, item in items_of(items): if isinstance(item, Traversable): # noinspection PyProtectedMember assert not item._is_bound item._name = key unbound_items[key] = item elif isinstance(item, dict): if key in unbound_items: _unapplied_config[key] = item else: item = setdefaults_path( Namespace(), item, call_target__cls=cls, _name=key, ) unbound_items[key] = item() else: assert unknown_types_fall_through or item is None, f'I got {type(item)} when creating a {cls.__name__}.{key}, but I was expecting Traversable or dict' unbound_items[key] = item for k, v in items_of(Namespace(_unapplied_config)): unbound_items[k] = unbound_items[k].reinvoke(v) # noinspection PyProtectedMember assert unbound_items[k]._name is not None to_delete = { k for k, v in items_of(unbound_items) if v is None } for k in to_delete: del unbound_items[k] sort_after(unbound_items) set_declared_member(container, name, unbound_items) setattr(container, name, NotBoundYet(container, name))