Exemple #1
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
Exemple #2
0
def test_collect_sets_name():
    class MyBasket(Basket):
        orange = Fruit(taste='sour')

    basket = MyBasket()
    assert declared_members(basket).fruits.orange._name == 'orange'

    basket = MyBasket(fruits__orange=Fruit(taste='sour'))
    assert declared_members(basket).fruits.orange._name == 'orange'
Exemple #3
0
    def on_bind(self) -> None:
        bind_members(self, name='filters')

        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'])):
            set_and_remember_for_reinvoke(declared_members(
                self.form).fields[FREETEXT_SEARCH_NAME],
                                          include=True)

        declared_fields = declared_members(self.form)['fields']
        for name, filter in items(self.filters):
            is_valid, message = filter.is_valid_filter(name=name,
                                                       filter=filter)
            assert is_valid, message
            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,
                    help__include=False,
                )
                declared_fields[name] = reinvoke(declared_fields[name], field)
        set_declared_member(self.form, 'fields', declared_fields)

        # Remove fields from the form that correspond to non-included filters
        declared_filters = declared_members(self)['filters']
        for name, field in items(declared_fields):
            if name == FREETEXT_SEARCH_NAME:
                continue
            # We need to check if it's in declared_filters first, otherwise we remove any injected fields
            if name in declared_filters and name not in self.filters:
                set_and_remember_for_reinvoke(field, include=False)

        bind_members(self, name='endpoints')

        self.form = self.form.bind(parent=self)
        self._bound_members.form = self.form

        self.advanced = self._bound_members.advanced = self.advanced.bind(
            parent=self)

        self.form_container = self.form_container.bind(parent=self)

        self.filter_name_by_query_name = {
            x.query_name: name
            for name, x in items(self.filters)
        }
Exemple #4
0
    def rows(node, name='', path=None):
        if path is None:
            path = []
        is_struct = type(node) is Struct
        is_bound = getattr(node, '_is_bound', False)

        try:
            p = node.iommi_path if is_bound else None
        except PathNotFoundException:
            p = None

        type_name = type(node).__name__ if not is_struct else None
        base_type_name = type_name
        if isinstance(node, Members) and node._declared_members:
            if name == 'parts':
                member_type = 'Part'
            else:
                member_type = type(list(values(declared_members(node)))[0]).__name__
            type_name = f'Members[{member_type}]'

        children = []
        if isinstance(node, dict):
            children = list(node.items())
        elif isinstance(node, Traversable):
            children = [
                (
                    k,
                    node.iommi_bound_members().get(k, v)
                )
                for k, v in items(declared_members(node))
            ]

        if (isinstance(node, Members) or isinstance(node, dict)) and not children:
            return

        yield Struct(
            name=name,
            obj=node,
            type=type_name,
            base_type=base_type_name,
            path=p,
            dunder_path='__'.join(path),
            included=is_bound
        )

        for k, v in children:
            yield from rows(v, name=k, path=path + [k])
Exemple #5
0
def test_from_model_foreign_key():
    class MyQuery(Query):
        class Meta:
            filters = Query.filters_from_model(model=Bar)

    t = MyQuery().bind(request=req('get'))
    assert list(declared_members(t).filters.keys()) == ['id', 'foo']
    assert isinstance(t.filters['foo'].choices, QuerySet)
Exemple #6
0
def test_page_constructor():
    class MyPage(Page):
        h1 = html.h1()

    my_page = MyPage(parts__foo=html.div(_name='foo'), parts__bar=html.div())

    assert ['h1', 'foo', 'bar'] == list(declared_members(my_page).parts.keys())
    my_page = my_page.bind(request=None)
    assert ['h1', 'foo', 'bar'] == list(my_page.parts.keys())
Exemple #7
0
def bind_members(parent: Traversable, *, name: str, cls=Members, unknown_types_fall_through=False) -> None:
    m = cls(
        _name=name,
        _declared_members=declared_members(parent)[name],
        unknown_types_fall_through=unknown_types_fall_through,
    )
    m = m.bind(parent=parent)
    setattr(parent._bound_members, name, m)
    setattr(parent, name, m._bound_members)
Exemple #8
0
    def __init__(self, **kwargs):
        super(Advanced, self).__init__(**kwargs)

        toggle = setdefaults_path(
            Namespace(),
            self.toggle,
            _name='toggle',
            call_target=Action,
            attrs__href='#',
            attrs__class__iommi_query_toggle_simple_mode=True,
            attrs={'data-advanced-mode': 'simple'},
            display_name=gettext('Switch to advanced search'),
        )
        self.toggle = declared_members(self).toggle = toggle()
Exemple #9
0
def bind_members(parent: Traversable, *, name: str, cls=Members, unknown_types_fall_through=False, lazy=True) -> None:
    """
    This is the companion function to `collect_members`. It is used at bind
    time to recursively (and by default lazily(!)) bind the parts of a container.
    """
    m = cls(
        _name=name,
        _declared_members=declared_members(parent)[name],
        unknown_types_fall_through=unknown_types_fall_through,
    )
    assert parent._is_bound
    m = m.bind(parent=parent)
    setattr(parent._bound_members, name, m)
    setattr(parent, name, m._bound_members)
    if not lazy:
        _force_bind_all(m._bound_members)
Exemple #10
0
def test_promote_str_to_fragment_for_page():
    class MyPage(Page):
        foo = 'asd'

    page = MyPage()
    assert isinstance(declared_members(page).parts.foo, Fragment)
Exemple #11
0
    def __init__(self,
                 *,
                 model=None,
                 rows=None,
                 filters=None,
                 _filters_dict=None,
                 auto,
                 **kwargs):
        assert isinstance(filters, dict)

        if auto:
            auto = QueryAutoConfig(**auto)
            auto_model, auto_rows, filters = self._from_model(
                model=auto.model,
                rows=auto.rows,
                filters=filters,
                include=auto.include,
                exclude=auto.exclude,
            )

            assert model is None, "You can't use the auto feature and explicitly pass model. " \
                                  "Either pass auto__model, or we will set the model for you from auto__rows"
            model = auto_model

            if rows is None:
                rows = auto_rows

        model, rows = model_and_rows(model, rows)

        setdefaults_path(
            kwargs,
            form__call_target=self.get_meta().form_class,
        )

        self._form = None
        self.query_advanced_value = None
        self.query_error = None

        super(Query, self).__init__(model=model, rows=rows, **kwargs)

        collect_members(self,
                        name='filters',
                        items=filters,
                        items_dict=_filters_dict,
                        cls=self.get_meta().member_class)

        field_class = self.get_meta().form_class.get_meta().member_class

        declared_fields = Struct()
        declared_fields[FREETEXT_SEARCH_NAME] = field_class(
            _name=FREETEXT_SEARCH_NAME,
            display_name='Search',
            required=False,
            include=False,
        )

        for name, filter in items(declared_members(self).filters):
            if filter.attr is None and getattr(filter.value_to_q,
                                               'iommi_needs_attr', False):
                continue
            field = setdefaults_path(
                Namespace(),
                filter.field,
                _name=name,
                model_field=filter.model_field,
                attr=name if filter.attr is MISSING else filter.attr,
                call_target__cls=field_class,
            )
            declared_fields[name] = field()

        # noinspection PyCallingNonCallable
        self.form: Form = self.form(
            _name='form',
            _fields_dict=declared_fields,
            attrs__method='get',
            actions__submit__attrs__value='Filter',
        )
        declared_members(self).form = self.form

        self.advanced_simple_toggle = Action(
            attrs__href='#',
            attrs__class__iommi_query_toggle_simple_mode=True,
            attrs={'data-advanced-mode': 'simple'},
            display_name='Switch to advanced search',
        )

        self.form_container = self.form_container()

        # Filters need to be at the end to not steal the short names
        set_declared_member(self, 'filters',
                            declared_members(self).pop('filters'))
Exemple #12
0
def test_collect_from_declarative():
    class MyBasket(Basket):
        orange = Fruit(taste='sour')

    basket = MyBasket()
    assert declared_members(basket).fruits.orange.taste == 'sour'
Exemple #13
0
def test_collect_from_arg():
    basket = Basket(fruits__banana__taste="sweet")
    assert declared_members(basket).fruits.banana.taste == 'sweet'
Exemple #14
0
def test_empty_collect():
    assert declared_members(Basket()).fruits == {}
Exemple #15
0
def test_none_members_should_be_discarded_after_being_allowed_through():
    class MyBasket(Basket):
        orange = Fruit(taste='sour')

    basket = MyBasket(fruits__orange=None)
    assert 'orange' not in declared_members(basket).fruits
Exemple #16
0
    def __init__(self,
                 *,
                 model=None,
                 rows=None,
                 filters=None,
                 _filters_dict=None,
                 auto=None,
                 **kwargs):
        assert isinstance(filters, dict)

        if auto:
            auto = QueryAutoConfig(**auto)
            auto_model, auto_rows, filters = self._from_model(
                model=auto.model,
                rows=auto.rows,
                filters=filters,
                include=auto.include,
                exclude=auto.exclude,
            )

            assert model is None, "You can't use the auto feature and explicitly pass model. " \
                                  "Either pass auto__model, or we will set the model for you from auto__rows"
            model = auto_model

            if rows is None:
                rows = auto_rows

        model, rows = model_and_rows(model, rows)

        setdefaults_path(
            kwargs,
            form__call_target=self.get_meta().form_class,
        )

        self.query_advanced_value = None
        self.query_error = None

        # Here we need to remove the freetext config from kwargs because we want to
        # handle it differently from the other fields.
        # BUT It's not safe to modify kwargs deeply! Because reinvoke() is evil:
        # It will call init again with the same kwargs + whatever additional kwargs
        # might have come from a parent or styling info.
        freetext_config = kwargs.get('form', {}).get('fields',
                                                     {}).get('freetext', {})
        if 'form' in kwargs and 'fields' in kwargs[
                'form'] and 'freetext' in kwargs['form']['fields']:
            # copy (just) the namespace so we can safely remove freetext from it
            kwargs = Namespace(flatten(Namespace(kwargs)))
            freetext_config = kwargs.get('form',
                                         {}).get('fields',
                                                 {}).pop('freetext', {})

        super(Query, self).__init__(model=model, rows=rows, **kwargs)

        collect_members(self,
                        name='filters',
                        items=filters,
                        items_dict=_filters_dict,
                        cls=self.get_meta().member_class)

        field_class = self.get_meta().form_class.get_meta().member_class

        declared_fields = Struct()
        declared_fields[FREETEXT_SEARCH_NAME] = field_class(
            _name=FREETEXT_SEARCH_NAME,
            display_name=gettext('Search'),
            required=False,
            include=False,
            help__include=False,
            **freetext_config)

        for name, filter in items(declared_members(self).filters):
            field = setdefaults_path(
                Namespace(),
                filter.field,
                _name=name,
                model_field=filter.model_field,
                attr=name if filter.attr is MISSING else filter.attr,
                call_target__cls=field_class,
                help__include=False,
            )
            declared_fields[name] = field()

        # noinspection PyCallingNonCallable
        self.form: Form = self.form(
            _name='form',
            _fields_dict=declared_fields,
            attrs__method='get',
            actions__submit=dict(
                attrs={'data-iommi-filter-button': ''},
                display_name=gettext('Filter'),
            ),
        )
        declared_members(self).form = self.form

        self.advanced = declared_members(self).advanced = self.advanced(
            _name='advanced')

        self.form_container = self.form_container(_name='form_container')

        # Filters need to be at the end to not steal the short names
        set_declared_member(self, 'filters',
                            declared_members(self).pop('filters'))
Exemple #17
0
def test_from_model_with_queryset():
    t = Query(auto__rows=Foo.objects.all()).bind(request=None)
    assert list(declared_members(t).filters.keys()) == ['id', 'foo']
    assert list(t.filters.keys()) == ['foo']
Exemple #18
0
def test_from_model_with_model_class():
    t = Query(auto__model=Foo).bind(request=None)
    assert list(declared_members(t).filters.keys()) == ['id', 'foo']
    assert list(t.filters.keys()) == ['foo']