Exemple #1
0
def form_example_4(request):
    ensure_objects()
    return Form.as_edit_page(instance=Foo.objects.all().first(),
                             actions=dict(
                                 foo=Action.submit(attrs__value='Foo'),
                                 bar=Action.submit(attrs__value='Bar'),
                                 back=Action(display_name='Back to index',
                                             attrs__href='/'),
                             ))
Exemple #2
0
def form_example_6(request):
    return Form.edit(auto__instance=Artist.objects.all().first(),
                     actions=dict(
                         foo=Action.submit(attrs__value='Foo'),
                         bar=Action.submit(attrs__value='Bar'),
                         a=Action.submit(attrs__value='Foo', group='x'),
                         b=Action.submit(attrs__value='Bar', group='x'),
                         back=Action(display_name='Back to index',
                                     attrs__href='/'),
                     ))
Exemple #3
0
def form_example_5(request):
    ensure_objects()
    return Form.as_create_page(
        model=Bar,
        fields__b__input__template='iommi/form/choice_select2.html',
        actions=dict(
            foo=Action.submit(attrs__value='Foo'),
            bar=Action.submit(attrs__value='Bar'),
            back=Action(display_name='Back to index', attrs__href='/'),
        ))
Exemple #4
0
def results(request, question_id):
    # question = get_object_or_404(Question, pk=question_id)
    # return render(request, 'polls/results.html', {'question': question})

    # OR

    # class ResultsView(generic.DetailView):
    #     model = Question
    #     template_name = 'polls/results.html'

    # <h1>{{ question.question_text }}</h1>
    #
    # <ul>
    # {% for choice in question.choice_set.all %}
    #     <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
    # {% endfor %}
    # </ul>
    #
    # <a href="{% url 'polls:detail' question.id %}">Vote again?</a>

    question = get_object_or_404(Question, pk=question_id)
    return Table(
        title=str(question),
        auto__rows=question.choice_set.all(),
        auto__exclude=['question'],
        actions__vote_again=Action(display_name='Vote again?',
                                   attrs__href=reverse('polls:vote',
                                                       args=(question_id, ))),
        actions_below=True,
    )
Exemple #5
0
def test_render():
    action = Action(
        _name='do_it',
        attrs__href='#'
    ).bind()
    assert_renders(action, '''
        <a href="#"> Do it </a>
    ''')
Exemple #6
0
def test_render_class():
    action = Action(
        _name='do_it',
        attrs__class__foo=True
    ).bind()
    assert_renders(action, '''
        <a class="foo">Do it</a>
    ''')
Exemple #7
0
def test_render_icon():
    submit = Action.icon(
        icon='flower',
        display_name='Name',
    ).bind()
    assert_renders(submit, '''
       <a> <i class="fa fa-flower"> </i> Name </a>
    ''')
Exemple #8
0
def test_render_submit():
    submit = Action.submit(display_name='Do it').bind()
    assert_renders(
        submit,
        '''
       <button accesskey="s" name="-">Do it</button>
    ''',
    )
Exemple #9
0
def test_render_button():
    submit = Action.button(_name='do_it').bind()
    assert_renders(
        submit,
        '''
       <button> Do it </button>
    ''',
    )
Exemple #10
0
def test_render_input():
    action = Action(_name='do_it', tag='input', attrs__href='#').bind()
    assert_renders(
        action,
        '''
        <input href="#" value="Do it">
    ''',
    )
Exemple #11
0
class IndexPage(ExamplesPage):
    header = html.h1('Form examples')
    description = html.p('Some examples of iommi Forms')

    examples = example_links(examples)

    all_fields = Action(
        display_name='Example with all types of fields',
        attrs__href='all_fields',
    )
Exemple #12
0
 class Meta:
     sortable = False
     actions = dict(
         a=Action(display_name='Foo',
                  attrs__href='/foo/',
                  include=lambda table, **_: table.rows is not rows),
         b=Action(display_name='Bar',
                  attrs__href='/bar/',
                  include=lambda table, **_: table.rows is rows),
         c=Action(display_name='Baz',
                  attrs__href='/bar/',
                  group='Other'),
         d=dict(display_name='Qux', attrs__href='/bar/', group='Other'),
         e=Action.icon('icon_foo',
                       display_name='Icon foo',
                       attrs__href='/icon_foo/'),
         f=Action.icon('icon_bar',
                       icon_classes=['lg'],
                       display_name='Icon bar',
                       attrs__href='/icon_bar/'),
         g=Action.icon('icon_baz',
                       icon_classes=['one', 'two'],
                       display_name='Icon baz',
                       attrs__href='/icon_baz/'),
     )
Exemple #13
0
def example_links(examples):
    children = {}

    for i, example in enumerate(examples):
        n = i + 1
        children[f'example_{n}'] = html.p(
            Action(
                display_name=f'Example {n}: {example.description}',
                attrs__href=f'example_{n}',
            ),
            html.br(),
        )

    return Fragment(children=children)
Exemple #14
0
def example_links(examples):
    result = {}

    for i, example in enumerate(examples):
        n = i + 1
        result[f'example_{n}'] = html.p(
            Action(
                display_name=f'Example {n}: {example.description}',
                attrs__href=f'example_{n}',
            ),
            html.br(),
        )

    return result
Exemple #15
0
class IndexPage(ExamplesPage):
    header = html.h1('Form examples')
    description = html.p('Some examples of iommi Forms')
    all_fields = html.p(
        Action(
            display_name='Example with all types of fields',
            attrs__href='all_fields',
        ),
        html.br(),
        after='example_11',
    )

    class Meta:
        parts = example_links(examples)
Exemple #16
0
def test_action_groups():
    non_grouped, grouped = group_actions(dict(
        a=Action(),
        b=Action(),
        c=Action(group='a'),
        d=Action(group='a'),
        e=Action(group='a'),
        f=Action(group='b'),
        g=Action(group='b'),
    ))
    assert len(non_grouped) == 2
    assert len(grouped) == 2
    assert len(grouped[0][2]) == 3
    assert len(grouped[1][2]) == 2
Exemple #17
0
 class Meta:
     auto__model = Album
     page_size = 20
     columns__name__cell__url = lambda row, **_: row.get_absolute_url()
     columns__name__filter__include = True
     columns__year__filter__include = True
     columns__year__filter__field__include = False
     columns__artist__filter__include = True
     columns__edit = Column.edit(
         include=lambda request, **_: request.user.is_staff,
     )
     columns__delete = Column.delete(
         include=lambda request, **_: request.user.is_staff,
     )
     actions__create_album = Action(attrs__href='/albums/create/',
                                    display_name=_('Create album'))
Exemple #18
0
class IndexPage(ExamplesPage):
    header = html.h1('Table examples')

    description = html.p('Some examples of iommi tables')

    all_fields = html.p(
        Action(
            display_name='Example with all available types of columns',
            attrs__href='all_columns',
        ),
        html.br(),
        after='example_6',
    )

    class Meta:
        parts = example_links(examples)
Exemple #19
0
def index(request, path=''):
    p = (Path(settings.ARCHIVE_PATH) / path).absolute()
    assert str(p).startswith(str(Path(settings.ARCHIVE_PATH).absolute())), 'Someone is trying to hack the site'

    if p.is_file():
        return FileResponse(open(p, 'rb'), filename=p.name)
    rows = [
        x
        for x in p.glob('*')
        if not x.name.startswith('.')
    ]

    if request.GET.get('gallery') is not None:
        images = [
            x
            for x in rows
            if is_image(x)
        ]
        return Table.div(
            rows=sorted(images, key=lambda x: x.name.lower()),
            page_size=None,
            columns__name=Column(cell__format=lambda row, **_: mark_safe(f'<img src="{row.name}" style="max-width: 100%">'))
        )

    return Table(
        columns=dict(
            icon=Column(
                display_name='',
                header__attrs__style__width='25px',
                attr=None,
                cell__format=lambda row, **_: mark_safe('<i class="far fa-folder"></i>') if row.is_dir() else ''
            ),
            name=Column(
                cell__url=lambda row, **_: f'{row.name}/' if row.is_dir() else row.name,
            ),
        ),
        page_size=None,
        rows=sorted(rows, key=lambda x: x.name.lower()),
        actions__gallery=Action(attrs__href='?gallery')
    )
Exemple #20
0
def test_display_name_to_value_attr_but_attr_overrides():
    assert Action.delete(
        display_name='foo', attrs__value='bar').bind(request=None).__html__(
        ) == '<input accesskey="s" name="-" type="submit" value="bar">'
Exemple #21
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 #22
0
class Query(Part):
    """
    Declare a query language. Example:

    .. code:: python

        class CarQuery(Query):
            make = Filter.choice(choices=['Toyota', 'Volvo', 'Ford'])
            model = Filter()

        query_set = Car.objects.filter(
            CarQuery().bind(request=request).get_q()
        )
    """

    form: Namespace = Refinable()
    model: Type[Model] = Refinable(
    )  # model is evaluated, but in a special way so gets no EvaluatedRefinable type
    rows = Refinable()
    template: Union[str, Template] = EvaluatedRefinable()
    form_container: Fragment = EvaluatedRefinable()

    member_class = Refinable()
    form_class = Refinable()

    class Meta:
        member_class = Filter
        form_class = Form

    @reinvokable
    @dispatch(
        endpoints__errors__func=default_endpoint__errors,
        filters=EMPTY,
        auto=EMPTY,
        form__attrs={
            'data-iommi-errors':
            lambda query, **_: query.endpoints.errors.iommi_path
        },
        form_container__call_target=Fragment,
        form_container__tag='span',
        form_container__attrs__class__iommi_query_form_simple=True,
    )
    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'))

    @dispatch(
        render__call_target=render_template, )
    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 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,
                    help__include=False,
                )
                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

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

    def own_evaluate_parameters(self):
        return dict(query=self)

    def get_advanced_query_param(self):
        return '-' + path_join(self.iommi_path, 'query')

    def parse_query_string(self, query_string: str) -> Q:
        assert self._is_bound
        query_string = query_string.strip()
        if not query_string:
            return Q()
        parser = self._create_grammar()
        try:
            tokens = parser.parseString(query_string, parseAll=True)
        except ParseException as e:
            raise QueryException('Invalid syntax for query')
        return self._compile(tokens)

    def _compile(self, tokens) -> Q:
        items = []
        for token in tokens:
            if isinstance(token, ParseResults):
                items.append(self._compile(token))
            elif isinstance(token, Q):
                items.append(token)
            elif token in ('and', 'or'):
                items.append(token)
        return self._rpn_to_q(self._tokens_to_rpn(items))

    @staticmethod
    def _rpn_to_q(tokens):
        stack = []
        for each in tokens:
            if isinstance(each, Q):
                stack.append(each)
            else:
                op = each
                # infix right hand operator is on the top of the stack
                right, left = stack.pop(), stack.pop()
                stack.append(left & right if op == 'and' else left | right)
        assert len(stack) == 1
        return stack[0]

    @staticmethod
    def _tokens_to_rpn(tokens):
        # Convert a infix sequence of Q objects and 'and/or' operators using
        # dijkstra shunting yard algorithm into RPN
        if len(tokens) == 1:
            return tokens
        result_q, stack = [], []
        for token in tokens:
            assert token is not None
            if isinstance(token, Q):
                result_q.append(token)
            elif token in PRECEDENCE:
                p1 = PRECEDENCE[token]
                while stack:
                    t2, p2 = stack[-1]
                    if p1 <= p2:
                        stack.pop()
                        result_q.append(t2)
                    else:  # pragma: no cover
                        break  # pragma: no mutate
                stack.append((token, PRECEDENCE[token]))
        while stack:
            result_q.append(stack.pop()[0])
        return result_q

    def _create_grammar(self):
        """
        Pyparsing implementation of a where clause grammar based on http://pyparsing.wikispaces.com/file/view/simpleSQL.py

        The query language is a series of statements separated by AND or OR operators and parentheses can be used to group/provide
        precedence.

        A statement is a combination of three strings "<filter> <operator> <value>" or "<filter> <operator> <filter>".

        A value can be a string, integer or a real(floating) number or a (ISO YYYY-MM-DD) date.

        An operator must be one of "= != < > >= <= !:" and are translated into django __lte or equivalent suffixes.
        See self.as_q

        Example
        something < 10 AND other >= 2015-01-01 AND (foo < 1 OR bar > 1)

        """
        quoted_string_excluding_quotes = QuotedString(
            '"',
            escChar='\\').setParseAction(lambda token: StringValue(token[0]))
        and_ = Keyword('and', caseless=True)
        or_ = Keyword('or', caseless=True)
        binary_op = oneOf('=> =< = < > >= <= : != !:',
                          caseless=True).setResultsName('operator')

        # define query tokens
        identifier = Word(alphas, alphanums + '_$-.').setName('identifier')
        raw_value_chars = alphanums + '_$-+/$%*;?@[]\\^`{}|~.'
        raw_value = Word(raw_value_chars, raw_value_chars).setName('raw_value')
        value_string = quoted_string_excluding_quotes | raw_value

        # Define a where expression
        where_expression = Forward()
        binary_operator_statement = (identifier + binary_op +
                                     value_string).setParseAction(
                                         self._binary_op_to_q)
        unary_operator_statement = (identifier |
                                    (Char('!') + identifier)).setParseAction(
                                        self._unary_op_to_q)
        free_text_statement = quotedString.copy().setParseAction(
            self._freetext_to_q)
        operator_statement = binary_operator_statement | free_text_statement | unary_operator_statement
        where_condition = Group(operator_statement
                                | ('(' + where_expression + ')'))
        where_expression << where_condition + ZeroOrMore(
            (and_ | or_) + where_expression)

        # define the full grammar
        query_statement = Forward()
        query_statement << Group(where_expression).setResultsName("where")
        return query_statement

    def _unary_op_to_q(self, token):
        if len(token) == 1:
            (filter_name, ) = token
            value = 'true'
        else:
            (op, filter_name) = token
            value = 'false'
            if op != '!':  # pragma: no cover. You can't actually get here because you'll get a syntax error earlier
                raise QueryException(
                    f'Unknown unary filter operator "{op}", available operators: !'
                )

        filter = self.filters.get(filter_name.lower())
        if filter:
            if not filter.unary:
                raise QueryException(
                    f'"{filter_name}" is not a unary filter, you must use it like "{filter_name}=something"'
                )
            result = filter.value_to_q(filter=filter,
                                       op='=',
                                       value_string_or_f=value)
            return result
        raise QueryException(
            f'Unknown unary filter "{filter_name}", available filters: {", ".join(list(keys(self.filters)))}'
        )

    def _binary_op_to_q(self, token):
        """
        Convert a parsed token of filter_name OPERATOR filter_name into a Q object
        """
        assert self._is_bound
        filter_name, op, value_string_or_filter_name = token

        if filter_name.endswith('.pk'):
            filter = self.filters.get(filter_name.lower()[:-len('.pk')])
            if op != '=':
                raise QueryException(
                    'Only = is supported for primary key lookup')

            try:
                pk = int(value_string_or_filter_name)
            except ValueError:
                raise QueryException(
                    f'Could not interpret {value_string_or_filter_name} as an integer'
                )

            return Q(**{f'{filter.attr}__pk': pk})

        filter = self.filters.get(filter_name.lower())
        if filter:
            if isinstance(value_string_or_filter_name, str) and not isinstance(
                    value_string_or_filter_name, StringValue
            ) and value_string_or_filter_name.lower() in self.filters:
                value_string_or_f = F(
                    self.filters[value_string_or_filter_name.lower()].attr)
            else:
                value_string_or_f = value_string_or_filter_name
            try:
                result = filter.value_to_q(filter=filter,
                                           op=op,
                                           value_string_or_f=value_string_or_f)
            except ValidationError as e:
                raise QueryException(f'{e.message}')
            if result is None:
                raise QueryException(
                    f'Unknown value "{value_string_or_f}" for filter "{filter._name}"'
                )
            return result
        raise QueryException(
            f'Unknown filter "{filter_name}", available filters: {list(keys(self.filters))}'
        )

    def _freetext_to_q(self, token):
        if all(not v.freetext for v in values(self.filters)):
            raise QueryException('There are no freetext filters available')
        assert len(token) == 1
        token = token[0].strip('"')

        return reduce(operator.or_, [
            Q(
                **{
                    filter.attr + '__' + filter.query_operator_to_q_operator(':'):
                    token
                }) for filter in values(self.filters) if filter.freetext
        ])

    def get_query_string(self):
        """
        Based on the data in the request, return the equivalent query string that you can use with parse_query_string() to create a query set.
        """
        form = self.form
        request = self.get_request()

        if request is None:
            return ''

        if request_data(request).get(self.get_advanced_query_param(),
                                     '').strip():
            return request_data(request).get(self.get_advanced_query_param())
        elif form.is_valid():

            def expr(field, is_list, value):
                if is_list:
                    return '(' + ' OR '.join([
                        expr(field, is_list=False, value=x)
                        for x in field.value
                    ]) + ')'
                return build_query_expression(field=field,
                                              filter=self.filters[field._name],
                                              value=value)

            result = [
                expr(field, field.is_list, field.value)
                for field in values(form.fields)
                if field._name != FREETEXT_SEARCH_NAME and field.value not in (
                    None, '', [])
            ]

            if FREETEXT_SEARCH_NAME in form.fields:
                freetext = form.fields[FREETEXT_SEARCH_NAME].value
                if freetext:
                    result.append('(%s)' % ' or '.join([
                        f'{filter._name}:{to_string_surrounded_by_quote(freetext)}'
                        for filter in values(self.filters) if filter.freetext
                    ]))
            return ' and '.join(result)
        else:
            return ''

    def get_q(self):
        """
        Create a query set based on the data in the request.
        """
        try:
            return self.parse_query_string(self.get_query_string())
        except QueryException as e:
            self.query_error = str(e)
            raise

    @classmethod
    @dispatch(
        filters=EMPTY, )
    def filters_from_model(cls, filters, **kwargs):
        return create_members_from_model(
            member_class=cls.get_meta().member_class,
            member_params_by_member_name=filters,
            **kwargs)

    @classmethod
    @dispatch(
        filters=EMPTY, )
    def _from_model(cls,
                    *,
                    rows=None,
                    model=None,
                    filters,
                    include=None,
                    exclude=None):
        assert rows is None or isinstance(rows, QuerySet), \
            'auto__rows needs to be a QuerySet for filter generation to work. ' \
            'If it needs to be a lambda, provide a model with auto__model for filter generation, ' \
            'and pass the lambda as rows.'

        model, rows = model_and_rows(model, rows)
        assert model is not None or rows is not None, "auto__model or auto__rows must be specified"
        filters = cls.filters_from_model(model=model,
                                         include=include,
                                         exclude=exclude,
                                         filters=filters)
        return model, rows, filters
Exemple #23
0
 class Meta:
     page_size = 3
     # And register a button that will get the selection passed in its post_handler
     bulk__actions__print = Action.primary(
         display_name='print me',
         post_handler=bulk__actions__print__post_handler)
Exemple #24
0
def test_template():
    assert Action(template=Template('{{action.group}}'), group='foo').bind(request=None).__html__() == 'foo'
Exemple #25
0
def test_render_submit():
    submit = Action.submit(display_name='Do it').bind()
    assert_renders(
        submit, '''
       <input accesskey="s" name="-" type="submit" value="Do it"/>
    ''')
Exemple #26
0
def test_delete_action():
    assert Action.delete().bind(request=None).__html__() == '<button accesskey="s" name="-">Submit</button>'
Exemple #27
0
def test_icon_action():
    assert Action.icon('foo', display_name='dn').bind(request=None).__html__() == '<a><i class="fa fa-foo"></i> dn</a>'
Exemple #28
0
def test_icon_action_with_icon_classes():
    assert Action.icon('foo', display_name='dn', icon_classes=['a', 'b']).bind(request=None).__html__() == '<a><i class="fa fa-foo fa-a fa-b"></i> dn</a>'
Exemple #29
0
def test_display_name_to_value_attr():
    assert Action.delete(display_name='foo').bind(request=None).__html__() == '<button accesskey="s" name="-">foo</button>'
Exemple #30
0
def test_lambda_tag():
    assert Action(tag=lambda action, **_: 'foo', display_name='').bind(request=None).__html__() == '<foo></foo>'