예제 #1
0
def test_setdefaults_path_ordering():
    expected = Struct(x=Struct(y=17, z=42))

    actual_foo = setdefaults_path(
        Struct(), OrderedDict([
            ('x', {
                'z': 42
            }),
            ('x__y', 17),
        ]))
    assert actual_foo == expected

    actual_bar = setdefaults_path(
        Struct(), OrderedDict([
            ('x__y', 17),
            ('x', {
                'z': 42
            }),
        ]))
    assert actual_bar == expected
예제 #2
0
    def _prepare_headers(self):
        headers = prepare_headers(self.request, self.shown_bound_columns)

        # The id(header) and the type(x.display_name) stuff is to make None not be equal to None in the grouping
        header_groups = []

        class HeaderGroup(Struct):
            def render_css_class(self):
                return render_class(self.attrs['class'])

        for group_name, group_iterator in groupby(
                headers, key=lambda header: header.group or id(header)):

            header_group = list(group_iterator)

            header_groups.append(
                HeaderGroup(display_name=group_name,
                            sortable=False,
                            colspan=len(header_group),
                            attrs=Struct({'class': Struct(superheader=True)})))

            for x in header_group:
                x.attrs['class']['subheader'] = True
                if x.is_sorting:
                    x.attrs['class']['sorted_column'] = True

            header_group[0].attrs['class']['first_column'] = True

        if header_groups:
            header_groups[0].attrs['class']['first_column'] = True

        for x in header_groups:
            if not isinstance(x.display_name, string_types):
                x.display_name = ''
        if all([x.display_name == '' for x in header_groups]):
            header_groups = []

        self.header_levels = [header_groups, headers
                              ] if len(header_groups) > 1 else [headers]
        return headers
예제 #3
0
    def __init__(self, table, row, row_index):
        self.table = table
        """ :type : Table """
        self.row = row
        """ :type : object """
        self.row_index = row_index

        args = Struct(
            evaluate_recursive(extract_subkeys(table.Meta, 'row'),
                               table=table,
                               row=row))
        self.template = args.template
        self.attrs = args.attrs
예제 #4
0
 def generate_variables():
     for column in self.bound_columns:
         if column.query.show:
             query_kwargs = setdefaults_path(
                 Struct(), column.query,
                 dict(
                     name=column.name,
                     gui__label=column.display_name,
                     attr=column.attr,
                     model=column.table.Meta.model,
                 ), {
                     'class': Variable,
                 })
             yield query_kwargs.pop('class')(**query_kwargs)
예제 #5
0
def get_meta(cls):
    """
        Collect all members of any contained :code:`Meta` class declarations from the given class or any of its base classes.
        (Sub class values take precedence.)

        :type cls: class
        :rtype: Struct
    """
    merged_attributes = Struct()
    for class_ in reversed(cls.mro()):
        if hasattr(class_, 'Meta'):
            for key, value in class_.Meta.__dict__.items():
                merged_attributes[key] = value
    return merged_attributes
예제 #6
0
 def generate_bulk_fields():
     for column in self.bound_columns:
         if column.bulk.show:
             bulk_kwargs = setdefaults_path(
                 Struct(), column.bulk,
                 dict(
                     name=column.name,
                     attr=column.attr,
                     required=False,
                     empty_choice_tuple=(None, '', '---', True),
                     model=self.Meta.model,
                 ), {
                     'class': Field.from_model,
                 })
             if bulk_kwargs['class'] == Field.from_model:
                 bulk_kwargs['field_name'] = column.attr
             yield bulk_kwargs.pop('class')(**bulk_kwargs)
예제 #7
0
def test_file():
    class FooForm(Form):
        foo = Field.file(required=False)

    form = FooForm(data=Data(foo='1'))
    instance = Struct(foo=None)
    assert form.is_valid()
    form.apply(instance)
    assert instance.foo == '1'

    # Non-existent form entry should not overwrite data
    form = FooForm(data=Data(foo=''))
    assert form.is_valid(), {x.name: x.errors for x in form.fields}
    form.apply(instance)
    assert instance.foo == '1'

    form = FooForm(data=Data())
    assert form.is_valid(), {x.name: x.errors for x in form.fields}
    form.apply(instance)
    assert instance.foo == '1'
예제 #8
0
    def __init__(self, **kwargs):
        """
        :param name: the name of the column
        :param attr: What attribute to use, defaults to same as name. Follows django conventions to access properties of properties, so "foo__bar" is equivalent to the python code `foo.bar`. This parameter is based on the variable name of the Column if you use the declarative style of creating tables.
        :param display_name: the text of the header for this column. By default this is based on the `name` parameter so normally you won't need to specify it.
        :param css_class: CSS class of the header
        :param url: URL of the header. This should only be used if "sorting" is off.
        :param title: title/tool tip of header
        :param show: set this to False to hide the column
        :param sortable: set this to False to disable sorting on this column
        :param sort_key: string denoting what value to use as sort key when this column is selected for sorting. (Or callable when rendering a table from list.)
        :param sort_default_desc: Set to True to make table sort link to sort descending first.
        :param group: string describing the group of the header. If this parameter is used the header of the table now has two rows. Consecutive identical groups on the first level of the header are joined in a nice way.
        :param auto_rowspan: enable automatic rowspan for this column. To join two cells with rowspan, just set this auto_rowspan to True and make those two cells output the same text and we'll handle the rest.
        :param cell__template: name of a template file. The template gets arguments: `table`, `bound_column`, `bound_row`, `row` and `value`.
        :param cell__value: string or callable that receives kw arguments: `table`, `column` and `row`. This is used to extract which data to display from the object.
        :param cell__format: string or callable that receives kw arguments: `table`, `column`, `row` and `value`. This is used to convert the extracted data to html output (use `mark_safe`) or a string.
        :param cell__attrs: dict of attr name to callables that receive kw arguments: `table`, `column`, `row` and `value`.
        :param cell__url: callable that receives kw arguments: `table`, `column`, `row` and `value`.
        :param cell__url_title: callable that receives kw arguments: `table`, `column`, `row` and `value`.
        """

        kwargs.update({'attrs__class__' + c: True for c in kwargs.get('css_class', {})})

        setdefaults(kwargs, dict(
            bulk__show=False,
            query__show=False,
            extra=Struct(),
            attrs={},
            cell__template=None,
            cell__value=lambda table, column, row: getattr_path(row, evaluate(column.attr, table=table, column=column)),
            cell__format=default_cell_formatter,
            cell__attrs={},
            cell__url=None,
            cell__url_title=None
        ))
        namespaces = Struct(collect_namespaces(kwargs))
        namespaces.attrs = Struct(collect_namespaces(namespaces.attrs))
        namespaces.cell = Struct(collect_namespaces(namespaces.cell))
        namespaces.cell.attrs = Struct(collect_namespaces(namespaces.cell.attrs))

        namespaces.bulk = Struct(namespaces.bulk)
        namespaces.query = Struct(namespaces.query)
        namespaces.extra = Struct(namespaces.extra)

        setdefaults(namespaces.attrs, {'class': {}})
        setdefaults(namespaces.cell.attrs, {'class': {}})

        super(Column, self).__init__(**namespaces)
예제 #9
0
def test_sort_after_name_chaining():
    sorts_right([
        Struct(name='baz', after='foo', expected_position=2),
        Struct(name='foo', after='bar', expected_position=1),
        Struct(name='bar', after=0, expected_position=0),
    ])
예제 #10
0
def test_setdefaults_namespace_merge():
    actual = setdefaults_path(dict(x=1, y=Struct(z="foo")),
                              dict(y__a__b=17, y__z__c=True))
    expected = dict(x=1, y=Struct(a=Struct(b=17), z=Struct(foo=True, c=True)))
    assert expected == actual
예제 #11
0
def test_setdefault_string_value():
    actual = setdefaults_path(Struct(foo='barf'), foo__baz=False)
    expected = dict(foo=dict(barf=True, baz=False))
    assert expected == actual
예제 #12
0
def test_create_or_edit_object():
    # 1. View create form
    request = Struct(method='GET', META={}, user=Struct(is_authenticated=lambda: True))

    response = create_object(
        request=request,
        model=CreateOrEditObjectTest,
        form__f_int__initial=1,
        form__f_float__initial=lambda form, field: 2,
        render=lambda **kwargs: kwargs)
    assert response['context_instance']['object_name'] == 'create or edit object test'
    assert response['context_instance']['is_create'] == True
    form = response['context_instance']['form']
    assert not form.should_parse
    assert form.fields_by_name['f_int'].initial == 1
    assert form.fields_by_name['f_int'].errors == set()
    assert form.fields_by_name['f_int'].value == 1
    assert form.fields_by_name['f_float'].value == 2
    assert form.fields_by_name['f_bool'].value is None

    # 2. Create
    request.method = 'POST'
    request.POST = {
        'f_int': '3',
        'f_float': '5.1',
        'f_bool': 'True',
    }
    create_object(
        request=request,
        model=CreateOrEditObjectTest,
        render=lambda **kwargs: kwargs)
    assert get_saved_something() is not None
    assert get_saved_something().f_int == 3
    assert get_saved_something().f_float == 5.1
    assert get_saved_something().f_bool is True

    # 3. View edit form
    request.method = 'GET'
    del request.POST
    response = edit_object(
        request=request,
        instance=get_saved_something(),
        render=lambda **kwargs: kwargs)
    form = response['context_instance']['form']
    assert form.fields_by_name['f_int'].value == 3
    assert form.fields_by_name['f_float'].value == 5.1
    assert form.fields_by_name['f_bool'].value is True

    # 4. Edit
    request.method = 'POST'
    request.POST = {
        'f_int': '7',
        'f_float': '11.2',
        # Not sending a parameter in a POST is the same thing as false
    }
    edit_object(
        request=request,
        instance=get_saved_something(),
        render=lambda **kwargs: kwargs)
    assert get_saved_something() is not None
    assert get_saved_something().f_int == 7
    assert get_saved_something().f_float == 11.2
    assert not get_saved_something().f_bool
예제 #13
0
def test_create_or_edit_object():
    # 1. View create form
    request = Struct(method='GET',
                     META={},
                     GET={},
                     user=Struct(is_authenticated=lambda: True))

    response = create_object(
        request=request,
        model=CreateOrEditObjectTest,
        form__field__f_int__initial=1,
        form__field__f_float__initial=lambda form, field: 2,
        render__context={'foo': 'FOO'},
        render=lambda **kwargs: kwargs)
    assert response['context_instance'][
        'object_name'] == 'create or edit object test'
    assert response['context_instance']['is_create'] is True
    form = response['context_instance']['form']
    assert response['context_instance']['foo'] == 'FOO'
    assert form.mode is INITIALS_FROM_GET
    assert form.fields_by_name['f_int'].initial == 1
    assert form.fields_by_name['f_int'].errors == set()
    assert form.fields_by_name['f_int'].value == 1
    assert form.fields_by_name['f_float'].value == 2
    assert form.fields_by_name['f_bool'].value is None
    assert set(form.fields_by_name.keys()) == {'f_int', 'f_float', 'f_bool'}

    # 2. Create
    request.method = 'POST'
    request.POST = {
        'f_int': '3',
        'f_float': '5.1',
        'f_bool': 'True',
        '-': '-',
    }
    create_object(request=request,
                  model=CreateOrEditObjectTest,
                  render=lambda **kwargs: kwargs)
    assert get_saved_something() is not None
    assert get_saved_something().f_int == 3
    assert get_saved_something().f_float == 5.1
    assert get_saved_something().f_bool is True

    # 3. View edit form
    request.method = 'GET'
    del request.POST
    response = edit_object(request=request,
                           instance=get_saved_something(),
                           render=lambda **kwargs: kwargs)
    form = response['context_instance']['form']
    assert form.get_errors() == {}
    assert form.fields_by_name['f_int'].value == 3
    assert form.fields_by_name['f_float'].value == 5.1
    assert form.fields_by_name['f_bool'].value is True

    # 4. Edit
    request.method = 'POST'
    request.POST = {
        'f_int': '7',
        'f_float': '11.2',
        '-': '-',
        # Not sending a parameter in a POST is the same thing as false
    }
    response = edit_object(
        request=request,
        instance=get_saved_something(),
        redirect=lambda form, **_: {'context_instance': {
            'form': form
        }},
        render=lambda **kwargs: kwargs)
    form = response['context_instance']['form']
    assert form.get_errors() == {}
    assert form.is_valid()
    assert get_saved_something() is not None
    assert get_saved_something().f_int == 7
    assert get_saved_something().f_float == 11.2
    assert not get_saved_something().f_bool
예제 #14
0
파일: __init__.py 프로젝트: boxed/tri.form
class Form(object):
    """
    Describe a Form. Example:

    .. code:: python

        class MyForm(Form):
            a = Field()
            b = Field.email()

        form = MyForm(data={})

    You can also create an instance of a form with this syntax if it's more convenient:

    .. code:: python

        form = MyForm(data={}, fields=[Field(name='a'), Field.email(name='b')])

    See tri.declarative docs for more on this dual style of declaration.
    """
    def __init__(self, request=None, data=None, instance=None, fields=None, model=None, post_validation=None, fields_dict=None, endpoint_dispatch_prefix='form', is_full_form=True):
        """
        :type fields: list of Field
        :type data: dict[basestring, basestring]
        :type model: django.db.models.Model
        """
        self.endpoint_dispatch_prefix = endpoint_dispatch_prefix
        self.is_full_form = is_full_form
        self.request = request
        if data is None and request:
            data = request.POST if request.method == 'POST' else request.GET

        if data is None:
            data = {}

        def unbound_fields():
            if fields is not None:
                for field in fields:
                    yield field
            for name, field in fields_dict.items():
                dict.__setitem__(field, 'name', name)
                yield field
        self.fields = sort_after([BoundField(f, self) for f in unbound_fields()])
        """ :type: list of BoundField"""

        if instance is not None:
            for field in self.fields:
                if field.attr:
                    initial = field.read_from_instance(field, instance)
                    if field.is_list:
                        field.initial_list = initial
                    else:
                        field.initial = initial

            self.instance = instance
        else:
            self.instance = None

        self.mode = FULL_FORM_FROM_REQUEST if '-' in data else INITIALS_FROM_GET
        if self.mode == INITIALS_FROM_GET and request:
            assert request.method == 'GET', 'Seems to be a POST but parameter "-" is not present'

        if data:
            for field in self.fields:
                if field.is_list:
                    try:
                        # django and similar
                        # noinspection PyUnresolvedReferences
                        raw_data_list = data.getlist(field.name)
                    except AttributeError:  # pragma: no cover
                        # werkzeug and similar
                        raw_data_list = data.get(field.name)

                    if raw_data_list and field.strip_input:
                        raw_data_list = [x.strip() for x in raw_data_list]

                    if raw_data_list is not None:
                        field.raw_data_list = raw_data_list
                else:
                    field.raw_data = data.get(field.name)
                    if field.raw_data and field.strip_input:
                        field.raw_data = field.raw_data.strip()

        self.post_validation = post_validation if post_validation is not None else lambda form: None
        self.fields_by_name = None
        """ :type: dict[str, BoundField] """
        self.style = None
        self.model = model
        """ :type model: django.db.models.Model """
        self._valid = None
        self.errors = set()
        self.evaluate()
        self.is_valid()
        """ :type: list of str """

    @staticmethod
    def fields_from_model(**kwargs):
        return create_members_from_model(default_factory=Field.from_model, **kwargs)

    @staticmethod
    @dispatch(
        field=EMPTY,
    )
    def from_model(data, model, field, instance=None, include=None, exclude=None, extra_fields=None, post_validation=None, **kwargs):
        """
        Create an entire form based on the fields of a model. To override a field parameter send keyword arguments in the form
        of "the_name_of_the_field__param". For example:

        .. code:: python

            class Foo(Model):
                foo = IntegerField()

            Form.from_model(data=request.GET, model=Foo, field__foo__help_text='Overridden help text')

        :param include: fields to include. Defaults to all
        :param exclude: fields to exclude. Defaults to none (except that AutoField is always excluded!)

        """
        fields = Form.fields_from_model(model=model, include=include, exclude=exclude, extra=extra_fields, db_field=field)
        return Form(data=data, model=model, instance=instance, fields=fields, post_validation=post_validation, **kwargs)

    def is_valid(self):
        if self._valid is None:
            self.validate()
            for field in self.fields:
                if field.errors:
                    self._valid = False
                    break
            else:
                self._valid = not self.errors
        return self._valid

    def parse_field_raw_value(self, field, raw_data):
        try:
            return field.parse(form=self, field=field, string_value=raw_data)
        except ValueError as e:
            assert str(e) != ''
            field.errors.add(str(e))
        except ValidationError as e:
            for message in e.messages:
                msg = "%s" % message
                assert msg != ''
                field.errors.add(msg)

    def parse(self):
        for field in self.fields:
            if not field.editable:
                continue

            if self.mode is INITIALS_FROM_GET and field.raw_data is None and field.raw_data_list is None:
                continue

            if field.is_list:
                if field.raw_data_list is not None:
                    field.parsed_data_list = [self.parse_field_raw_value(field, x) for x in field.raw_data_list]
                else:
                    field.parsed_data_list = None
            elif field.is_boolean:
                field.parsed_data = self.parse_field_raw_value(field, '0' if field.raw_data is None else field.raw_data)
            else:
                if field.raw_data == '' and field.parse_empty_string_as_none:
                    field.parsed_data = None
                elif field.raw_data is not None:
                    field.parsed_data = self.parse_field_raw_value(field, field.raw_data)
                else:
                    field.parsed_data = None

    def evaluate(self):
        for field in self.fields:
            field.evaluate()
        self.fields = [field for field in self.fields if should_show(field)]
        self.fields_by_name = Struct({field.name: field for field in self.fields})

    def validate(self):
        self.parse()

        for field in self.fields:
            if (not field.editable) or (self.mode is INITIALS_FROM_GET and field.raw_data is None and field.raw_data_list is None):
                if field.is_list:
                    field.value_list = field.initial_list
                else:
                    field.value = field.initial
                continue

            value = None
            value_list = None
            if field.is_list:
                if field.parsed_data_list is not None:
                    value_list = [self.validate_field_parsed_data(field, x) for x in field.parsed_data_list]
            else:
                if field.parsed_data is not None:
                    value = self.validate_field_parsed_data(field, field.parsed_data)

            if not field.errors:
                if self.mode is FULL_FORM_FROM_REQUEST and field.required and not value and not value_list:
                    field.errors.add('This field is required')
                else:
                    field.value = value
                    field.value_list = value_list

        for field in self.fields:
            field.post_validation(form=self, field=field)
        self.post_validation(form=self)
        return self

    def validate_field_parsed_data(self, field, value):
        is_valid, error = field.is_valid(
            form=self,
            field=field,
            parsed_data=value)
        if is_valid and not field.errors and field.parsed_data is not None:
            value = field.parsed_data
        elif not is_valid and self.mode:
            if not isinstance(error, set):
                error = {error}
            for e in error:
                assert error != ''
                field.errors.add(e)
        return value

    def add_error(self, msg):
        self.errors.add(msg)

    def __str__(self):
        return self.table()

    def compact(self):
        return self.render(template_name=None)

    def table(self):
        return self.render(style='table', template_name=None)

    def render(self, style='compact', template_name="tri_form/form.html"):
        """
        :type style: str| unicode
        :type template_name: str | unicode | None
        """
        self.style = style
        r = []
        for field in self.fields:
            context = {
                'form': self,
                'field': field,
            }
            if field.template_string is not None:
                r.append(get_template_from_string(field.template_string, origin='tri.form', name='Form.render').render(Context(context)))
            else:
                r.append(render_to_string(field.template.format(style=style), context))
        if self.is_full_form:
            r.append(AVOID_EMPTY_FORM)

        if template_name is None:
            return mark_safe('\n'.join(r))
        else:
            return render_to_string(
                context_instance=RequestContext(self.request, dict(form=self)),
                template_name=template_name,
            )

    def apply(self, instance):
        """
        Write the new values specified in the form into the instance specified.
        """
        assert self.is_valid()
        for field in self.fields:
            self.apply_field(instance=instance, field=field)

    @staticmethod
    def apply_field(instance, field):
        if not field.editable:
            field.value = field.initial
            field.value_list = field.initial_list

        if field.attr is not None:
            field.write_to_instance(field, instance, field.value_list if field.is_list else field.value)

    def get_errors(self):
        r = {}
        if self.errors:
            r['global'] = self.errors
        field_errors = {x.name: x.errors for x in self.fields if x.errors}
        if field_errors:
            r['fields'] = field_errors
        return r

    def endpoint_dispatch(self, key, value):
        if key.startswith('field__'):
            key = key[len('field__'):]
            field = self.fields_by_name.get(key, None)
            if field is not None:
                return field.endpoint_dispatch(form=self, field=field, value=value)
예제 #15
0
class Form(object):
    """
    Describe a Form. Example:

    .. code:: python

        class MyForm(Form):
            a = Field()
            b = Field.email()

        form = MyForm(data={})

    You can also create an instance of a form with this syntax if it's more convenient:

    .. code:: python

        form = MyForm(data={}, fields=[Field(name='a'), Field.email(name='b')])

    See tri.declarative docs for more on this dual style of declaration.
    """
    def __init__(self, request=None, data=None, instance=None, fields=None, model=None, post_validation=None, fields_dict=None, endpoint_dispatch_prefix='form'):
        """
        :type fields: list of Field
        :type data: dict[basestring, basestring]
        :type model: django.db.models.Model
        """
        self.endpoint_dispatch_prefix = endpoint_dispatch_prefix
        self.request = request
        if data is None and request:
            data = request.POST if request.method == 'POST' else request.GET

        if data is None:
            data = {}

        def unbound_fields():
            if fields is not None:
                for field in fields:
                    yield field
            for name, field in fields_dict.items():
                dict.__setitem__(field, 'name', name)
                yield field
        self.fields = sort_after([BoundField(f, self) for f in unbound_fields()])
        """ :type: list of BoundField"""

        if instance is not None:
            for field in self.fields:
                if field.attr:
                    initial = field.read_from_instance(field, instance)
                    if field.is_list:
                        field.initial_list = initial
                    else:
                        field.initial = initial

            self.instance = instance
        else:
            self.instance = None

        self.mode = FULL_FORM_FROM_REQUEST if '-' in data else INITIALS_FROM_GET
        if self.mode == INITIALS_FROM_GET and request:
            assert request.method == 'GET', 'Seems to be a POST but parameter "-" is not present'

        if data:
            for field in self.fields:
                if field.is_list:
                    try:
                        # django and similar
                        # noinspection PyUnresolvedReferences
                        raw_data_list = data.getlist(field.name)
                    except AttributeError:  # pragma: no cover
                        # werkzeug and similar
                        raw_data_list = data.get(field.name)

                    if raw_data_list and field.strip_input:
                        raw_data_list = [x.strip() for x in raw_data_list]

                    if raw_data_list is not None:
                        field.raw_data_list = raw_data_list
                else:
                    field.raw_data = data.get(field.name)
                    if field.raw_data and field.strip_input:
                        field.raw_data = field.raw_data.strip()

        self.post_validation = post_validation if post_validation is not None else lambda form: None
        self.fields_by_name = None
        """ :type: dict[str, BoundField] """
        self.style = None
        self.model = model
        """ :type model: django.db.models.Model """
        self._valid = None
        self.errors = set()
        self.evaluate()
        self.is_valid()
        """ :type: list of str """

    @staticmethod
    def fields_from_model(**kwargs):
        return create_members_from_model(default_factory=Field.from_model, **kwargs)

    @staticmethod
    @dispatch(
        field=EMPTY,
    )
    def from_model(data, model, field, instance=None, include=None, exclude=None, extra_fields=None, post_validation=None, **kwargs):
        """
        Create an entire form based on the fields of a model. To override a field parameter send keyword arguments in the form
        of "the_name_of_the_field__param". For example:

        .. code:: python

            class Foo(Model):
                foo = IntegerField()

            Form.from_model(data=request.GET, model=Foo, field__foo__help_text='Overridden help text')

        :param include: fields to include. Defaults to all
        :param exclude: fields to exclude. Defaults to none (except that AutoField is always excluded!)

        """
        fields = Form.fields_from_model(model=model, include=include, exclude=exclude, extra=extra_fields, db_field=field)
        return Form(data=data, model=model, instance=instance, fields=fields, post_validation=post_validation, **kwargs)

    def is_valid(self):
        if self._valid is None:
            self.validate()
            for field in self.fields:
                if field.errors:
                    self._valid = False
                    break
            else:
                self._valid = not self.errors
        return self._valid

    def parse_field_raw_value(self, field, raw_data):
        try:
            return field.parse(form=self, field=field, string_value=raw_data)
        except ValueError as e:
            assert str(e) != ''
            field.errors.add(str(e))
        except ValidationError as e:
            for message in e.messages:
                msg = "%s" % message
                assert msg != ''
                field.errors.add(msg)

    def parse(self):
        for field in self.fields:
            if not field.editable:
                continue

            if self.mode is INITIALS_FROM_GET and field.raw_data is None and field.raw_data_list is None:
                continue

            if field.is_list:
                if field.raw_data_list is not None:
                    field.parsed_data_list = [self.parse_field_raw_value(field, x) for x in field.raw_data_list]
                else:
                    field.parsed_data_list = None
            elif field.is_boolean:
                field.parsed_data = self.parse_field_raw_value(field, '0' if field.raw_data is None else field.raw_data)
            else:
                if field.raw_data == '' and field.parse_empty_string_as_none:
                    field.parsed_data = None
                elif field.raw_data is not None:
                    field.parsed_data = self.parse_field_raw_value(field, field.raw_data)
                else:
                    field.parsed_data = None

    def evaluate(self):
        for field in self.fields:
            field.evaluate()
        self.fields = [field for field in self.fields if should_show(field)]
        self.fields_by_name = Struct({field.name: field for field in self.fields})

    def validate(self):
        self.parse()

        for field in self.fields:
            if self.mode is INITIALS_FROM_GET and field.raw_data is None and field.raw_data_list is None:
                if field.is_list:
                    field.value_list = field.initial_list
                else:
                    field.value = field.initial
                continue

            if not field.editable:
                continue

            value = None
            value_list = None
            if field.is_list:
                if field.parsed_data_list is not None:
                    value_list = [self.validate_field_parsed_data(field, x) for x in field.parsed_data_list]
            else:
                if field.parsed_data is not None:
                    value = self.validate_field_parsed_data(field, field.parsed_data)

            if not field.errors:
                if self.mode is FULL_FORM_FROM_REQUEST and field.required and not value and not value_list:
                    field.errors.add('This field is required')
                else:
                    field.value = value
                    field.value_list = value_list

        for field in self.fields:
            field.post_validation(form=self, field=field)
        self.post_validation(form=self)
        return self

    def validate_field_parsed_data(self, field, value):
        is_valid, error = field.is_valid(
            form=self,
            field=field,
            parsed_data=value)
        if is_valid and not field.errors and field.parsed_data is not None:
            value = field.parsed_data
        elif not is_valid and self.mode:
            if not isinstance(error, set):
                error = {error}
            for e in error:
                assert error != ''
                field.errors.add(e)
        return value

    def add_error(self, msg):
        self.errors.add(msg)

    def __str__(self):
        return self.table()

    def compact(self):
        return self.render(template_name=None)

    def table(self):
        return self.render(style='table', template_name=None)

    def render(self, style='compact', template_name="tri_form/form.html"):
        """
        :type style: str| unicode
        :type template_name: str | unicode | None
        """
        self.style = style
        r = []
        for field in self.fields:
            context = {
                'form': self,
                'field': field,
            }
            if field.template_string is not None:
                r.append(get_template_from_string(field.template_string, origin='tri.form', name='Form.render').render(Context(context)))
            else:
                r.append(render_to_string(field.template.format(style=style), context))
        r.append(AVOID_EMPTY_FORM)

        if template_name is None:
            return mark_safe('\n'.join(r))
        else:
            return render_to_string(
                context_instance=RequestContext(self.request, dict(form=self)),
                template_name=template_name,
            )

    def apply(self, instance):
        """
        Write the new values specified in the form into the instance specified.
        """
        assert self.is_valid()
        for field in self.fields:
            self.apply_field(instance=instance, field=field)

    @staticmethod
    def apply_field(instance, field):
        if not field.editable:
            field.value = field.initial
            field.value_list = field.initial_list

        if field.attr is not None:
            field.write_to_instance(field, instance, field.value_list if field.is_list else field.value)

    def get_errors(self):
        r = {}
        if self.errors:
            r['global'] = self.errors
        field_errors = {x.name: x.errors for x in self.fields if x.errors}
        if field_errors:
            r['fields'] = field_errors
        return r

    def endpoint_dispatch(self, key, value):
        if key.startswith('field__'):
            key = key[len('field__'):]
            field = self.fields_by_name.get(key, None)
            if field is not None:
                return field.endpoint_dispatch(form=self, field=field, value=value)
예제 #16
0
def test_order_after_complete():
    sorts_right([
        # header1
        Struct(name='quux', expected_position=2),
        Struct(name='foo', expected_position=3),
        # header2
        Struct(name='bar', expected_position=6),
        Struct(name='asd', expected_position=7),
        Struct(name='header1', after=0, expected_position=0),
        Struct(name='header1b', after=0, expected_position=1),
        Struct(name='header2', after='foo', expected_position=4),
        Struct(name='header2.b', after='foo', expected_position=5),
        Struct(name='header3', after='quux2', expected_position=9),
        Struct(name='quux2', expected_position=8),
        # header3
        Struct(name='quux3', expected_position=10),
        Struct(name='quux4', expected_position=11),
        Struct(name='quux5', after=LAST, expected_position=12),
        Struct(name='quux6', after=LAST, expected_position=13),
    ])
예제 #17
0
def test_setdefaults_path_empty_marker():
    actual = setdefaults_path(Struct(), foo=EMPTY, bar__boink=EMPTY)
    expected = dict(foo={}, bar=dict(boink={}))
    assert expected == actual
예제 #18
0
def test_setdefaults_path_multiple_defaults():
    actual = setdefaults_path(Struct(), Struct(a=17, b=42), Struct(a=19,
                                                                   c=4711))
    expected = dict(a=17, b=42, c=4711)
    assert expected == actual
예제 #19
0
def test_setdefaults_path_empty_marker_copy():
    actual = setdefaults_path(Struct(), x=EMPTY)
    expected = dict(x={})
    assert expected == actual
    assert actual.x is not EMPTY
예제 #20
0
파일: views.py 프로젝트: boxed/great_apis
 def data():
     for app_name, models in apps.all_models.items():
         for name, cls in models.items():
             if app.get(app_name, {}).get(name, {}).get('show', True):
                 yield Struct(app_name=app_name, model_name=name, model=cls)
예제 #21
0
def test_sort_after_indexes():
    sorts_right([
        Struct(name='baz', after=1, expected_position=2),
        Struct(name='foo', after=0, expected_position=1),
        Struct(name='bar', after=-1, expected_position=0),
    ])
예제 #22
0
 def evaluate(self):
     for field in self.fields:
         field.evaluate()
     self.fields = [field for field in self.fields if should_show(field)]
     self.fields_by_name = Struct({field.name: field for field in self.fields})
예제 #23
0
def test_parse_errors():
    def post_validation(form):
        form.add_error('General snafu')

    form = MyTestForm(data=Data(party='foo',
                                username='******',
                                joined='foo',
                                staff='foo',
                                admin='foo',
                                a_date='fooasd',
                                a_time='asdasd',
                                **{'-': ''}),
                      post_validation=post_validation)

    assert not form.is_valid()

    assert form.errors == {'General snafu'}

    assert form.fields_by_name['party'].parsed_data == 'foo'
    assert form.fields_by_name['party'].errors == {
        'foo not in available choices'
    }
    assert form.fields_by_name['party'].value is None

    assert form.fields_by_name['username'].parsed_data == 'bar_foo'
    assert form.fields_by_name['username'].errors == {
        'Username must begin with "foo_"'
    }
    assert form.fields_by_name['username'].value is None

    assert form.fields_by_name['joined'].raw_data == 'foo'
    assert_one_error_and_matches_reg_exp(
        form.fields_by_name['joined'].errors,
        "time data u?'foo' does not match format u?'%Y-%m-%d %H:%M:%S'")
    assert form.fields_by_name['joined'].parsed_data is None
    assert form.fields_by_name['joined'].value is None

    assert form.fields_by_name['staff'].raw_data == 'foo'
    assert form.fields_by_name['staff'].parsed_data is None
    assert form.fields_by_name['staff'].value is None

    assert form.fields_by_name['admin'].raw_data == 'foo'
    assert form.fields_by_name['admin'].parsed_data is None
    assert form.fields_by_name['admin'].value is None

    assert form.fields_by_name['a_date'].raw_data == 'fooasd'
    assert_one_error_and_matches_reg_exp(
        form.fields_by_name['a_date'].errors,
        "time data u?'fooasd' does not match format u?'%Y-%m-%d'")
    assert form.fields_by_name['a_date'].parsed_data is None
    assert form.fields_by_name['a_date'].value is None

    assert form.fields_by_name['a_time'].raw_data == 'asdasd'
    assert_one_error_and_matches_reg_exp(
        form.fields_by_name['a_time'].errors,
        "time data u?'asdasd' does not match format u?'%H:%M:%S'")
    assert form.fields_by_name['a_time'].parsed_data is None
    assert form.fields_by_name['a_time'].value is None

    with pytest.raises(AssertionError):
        form.apply(Struct())
예제 #24
0
def test_request_data():
    r = Struct(method='POST', POST='POST', GET='GET')
    assert request_data(r) == 'POST'
    r.method = 'GET'
    assert request_data(r) == 'GET'
예제 #25
0
def test_initial_from_instance():
    assert Form(instance=Struct(a=Struct(b=7)),
                fields=[Field(name='a__b')]).fields[0].initial == 7
예제 #26
0
파일: __init__.py 프로젝트: boxed/tri.form
 def evaluate(self):
     for field in self.fields:
         field.evaluate()
     self.fields = [field for field in self.fields if should_show(field)]
     self.fields_by_name = Struct({field.name: field for field in self.fields})
예제 #27
0
def test_initial_list_from_instance():
    assert Form(instance=Struct(a=Struct(b=[7])),
                fields=[Field(name='a__b',
                              is_list=True)]).fields[0].initial_list == [7]
예제 #28
0
def test_create_or_edit_object():
    # 1. View create form
    request = Struct(method='GET', META={}, GET={}, user=Struct(is_authenticated=lambda: True))

    response = create_object(
        request=request,
        model=CreateOrEditObjectTest,
        form__field__f_int__initial=1,
        form__field__f_float__initial=lambda form, field: 2,
        render_context={'foo': 'FOO'},
        render=lambda **kwargs: kwargs)
    assert response['context_instance']['object_name'] == 'create or edit object test'
    assert response['context_instance']['is_create'] is True
    form = response['context_instance']['form']
    assert response['context_instance']['foo'] == 'FOO'
    assert form.mode is INITIALS_FROM_GET
    assert form.fields_by_name['f_int'].initial == 1
    assert form.fields_by_name['f_int'].errors == set()
    assert form.fields_by_name['f_int'].value == 1
    assert form.fields_by_name['f_float'].value == 2
    assert form.fields_by_name['f_bool'].value is None
    assert set(form.fields_by_name.keys()) == {'f_int', 'f_float', 'f_bool'}

    # 2. Create
    request.method = 'POST'
    request.POST = {
        'f_int': '3',
        'f_float': '5.1',
        'f_bool': 'True',
        '-': '-',
    }
    create_object(
        request=request,
        model=CreateOrEditObjectTest,
        render=lambda **kwargs: kwargs)
    assert get_saved_something() is not None
    assert get_saved_something().f_int == 3
    assert get_saved_something().f_float == 5.1
    assert get_saved_something().f_bool is True

    # 3. View edit form
    request.method = 'GET'
    del request.POST
    response = edit_object(
        request=request,
        instance=get_saved_something(),
        render=lambda **kwargs: kwargs)
    form = response['context_instance']['form']
    assert form.get_errors() == {}
    assert form.fields_by_name['f_int'].value == 3
    assert form.fields_by_name['f_float'].value == 5.1
    assert form.fields_by_name['f_bool'].value is True

    # 4. Edit
    request.method = 'POST'
    request.POST = {
        'f_int': '7',
        'f_float': '11.2',
        '-': '-',
        # Not sending a parameter in a POST is the same thing as false
    }
    response = edit_object(
        request=request,
        instance=get_saved_something(),
        redirect=lambda form, **_: {'context_instance': {'form': form}},
        render=lambda **kwargs: kwargs)
    form = response['context_instance']['form']
    assert form.get_errors() == {}
    assert form.is_valid()
    assert get_saved_something() is not None
    assert get_saved_something().f_int == 7
    assert get_saved_something().f_float == 11.2
    assert not get_saved_something().f_bool
예제 #29
0
def test_required():
    form = MyTestForm(request=Struct(method='POST', POST=Data({'-': '-'})))
    assert form.fields_by_name['a_date'].value is None
    assert form.fields_by_name['a_date'].errors == {'This field is required'}