Пример #1
0
 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)
Пример #2
0
    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
Пример #3
0
 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)
Пример #4
0
    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__()
Пример #5
0
 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)
Пример #6
0
    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))
Пример #7
0
 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)
Пример #8
0
 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)
Пример #9
0
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(), )
Пример #10
0
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
Пример #11
0
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)
Пример #12
0
 def info(cls, value, call_target=None, **kwargs):
     """
     Shortcut to create an info entry.
     """
     setdefaults_path(
         kwargs,
         initial=value,
     )
     return call_target(**kwargs)
Пример #13
0
 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)
Пример #14
0
 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)
Пример #15
0
    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)
Пример #16
0
    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())
Пример #17
0
    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)
Пример #18
0
    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)
Пример #19
0
    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)
Пример #20
0
    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)
Пример #21
0
def test_setdefaults_callable_backward_not_namespace():
    actual = setdefaults_path(
        Namespace(foo__x=17),
        foo=EMPTY,
    )
    expected = Namespace(foo__x=17)
    assert actual == expected
Пример #22
0
    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,
        )
Пример #23
0
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()))})
Пример #24
0
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)
Пример #25
0
    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
        )
Пример #26
0
    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
Пример #27
0
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
Пример #28
0
    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
            })
Пример #29
0
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,
    )
Пример #30
0
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))