Пример #1
0
    def search_all_fields(self, search):
        """
        Performs a very broad search across all supported fields with the given search
        query. If the primary key value matches then that result will be returned
        otherwise all field types other than link row and boolean fields are currently
        searched.

        :param search: The search query.
        :type search: str
        :return: The queryset containing the search queries.
        :rtype: QuerySet
        """

        filter_builder = FilterBuilder(filter_type=FILTER_TYPE_OR).filter(
            Q(id__contains=search)
        )
        for field_object in self.model._field_objects.values():
            field_name = field_object['name']
            model_field = self.model._meta.get_field(field_name)

            sub_filter = field_object['type'].contains_query(
                field_name,
                search,
                model_field,
                field_object['field']
            )
            filter_builder.filter(sub_filter)

        return filter_builder.apply_to_queryset(self)
Пример #2
0
    def apply_filters(self, view, queryset):
        """
        Applies the view's filter to the given queryset.

        :param view: The view where to fetch the fields from.
        :type view: View
        :param queryset: The queryset where the filters need to be applied to.
        :type queryset: QuerySet
        :raises ValueError: When the queryset's model is not a table model or if the
            table model does not contain the one of the fields.
        :return: The queryset where the filters have been applied to.
        :type: QuerySet
        """

        model = queryset.model

        # If the model does not have the `_field_objects` property then it is not a
        # generated table model which is not supported.
        if not hasattr(model, "_field_objects"):
            raise ValueError("A queryset of the table model is required.")

        # If the filter are disabled we don't have to do anything with the queryset.
        if view.filters_disabled:
            return queryset

        filter_builder = FilterBuilder(filter_type=view.filter_type)

        for view_filter in view.viewfilter_set.all():
            # If the to be filtered field is not present in the `_field_objects` we
            # cannot filter so we raise a ValueError.
            if view_filter.field_id not in model._field_objects:
                raise ValueError(
                    f"The table model does not contain field "
                    f"{view_filter.field_id}."
                )

            field_object = model._field_objects[view_filter.field_id]
            field_name = field_object["name"]
            model_field = model._meta.get_field(field_name)
            view_filter_type = view_filter_type_registry.get(view_filter.type)

            filter_builder.filter(
                view_filter_type.get_filter(
                    field_name, view_filter.value, model_field, field_object["field"]
                )
            )

        return filter_builder.apply_to_queryset(queryset)
Пример #3
0
def test_building_filter_with_and_type_ands_all_provided_qs_together(
        data_fixture):
    user = data_fixture.create_user()
    table = data_fixture.create_database_table(user=user)
    text_field = data_fixture.create_text_field(table=table,
                                                order=1,
                                                name="name")
    bool_field = data_fixture.create_boolean_field(table=table,
                                                   order=2,
                                                   name="is_active")

    model = table.get_model()
    row_1 = model.objects.create(**{
        f"field_{text_field.id}": "name",
        f"field_{bool_field.id}": True
    })
    model.objects.create(**{
        f"field_{text_field.id}": "name",
        f"field_{bool_field.id}": False
    })

    builder = FilterBuilder(filter_type=FILTER_TYPE_AND)
    builder.filter(Q(**{f"field_{text_field.id}": "name"}))
    builder.filter(Q(**{f"field_{bool_field.id}": True}))

    queryset = builder.apply_to_queryset(model.objects)
    assert queryset.count() == 1
    assert row_1 in queryset
Пример #4
0
def test_can_invert_an_annotated_q(data_fixture):
    user = data_fixture.create_user()
    table = data_fixture.create_database_table(user=user)
    text_field = data_fixture.create_text_field(table=table,
                                                order=1,
                                                name="name")
    another_text_field = data_fixture.create_text_field(table=table,
                                                        order=2,
                                                        name="surname")

    model = table.get_model()
    model.objects.create(
        **{
            f"field_{text_field.id}": "name",
            f"field_{another_text_field.id}": "other"
        })
    row_2 = model.objects.create(
        **{
            f"field_{text_field.id}": "eman",
            f"field_{another_text_field.id}": "extra"
        })
    row_3 = model.objects.create(
        **{
            f"field_{text_field.id}": "not_name",
            f"field_{another_text_field.id}": "not_other",
        })

    builder = FilterBuilder(filter_type=FILTER_TYPE_AND)
    q_to_invert = AnnotatedQ(
        annotation={"reversed_name": Reverse(f"field_{text_field.id}")},
        q={f"reversed_name": "eman"},
    )
    builder.filter(~q_to_invert)

    queryset = builder.apply_to_queryset(model.objects)
    assert queryset.count() == 2
    assert row_2 in queryset
    assert row_3 in queryset
Пример #5
0
def test_can_invert_an_annotated_q(data_fixture):
    user = data_fixture.create_user()
    table = data_fixture.create_database_table(user=user)
    text_field = data_fixture.create_text_field(table=table,
                                                order=1,
                                                name='name')
    another_text_field = data_fixture.create_text_field(table=table,
                                                        order=2,
                                                        name='surname')

    model = table.get_model()
    model.objects.create(
        **{
            f'field_{text_field.id}': 'name',
            f'field_{another_text_field.id}': 'other'
        })
    row_2 = model.objects.create(
        **{
            f'field_{text_field.id}': 'eman',
            f'field_{another_text_field.id}': 'extra'
        })
    row_3 = model.objects.create(
        **{
            f'field_{text_field.id}': 'not_name',
            f'field_{another_text_field.id}': 'not_other'
        })

    builder = FilterBuilder(filter_type=FILTER_TYPE_AND)
    q_to_invert = AnnotatedQ(
        annotation={'reversed_name': Reverse(f'field_{text_field.id}')},
        q={f'reversed_name': 'eman'})
    builder.filter(~q_to_invert)

    queryset = builder.apply_to_queryset(model.objects)
    assert queryset.count() == 2
    assert row_2 in queryset
    assert row_3 in queryset
Пример #6
0
def test_building_filter_with_annotated_qs_annotates_prior_to_filter(
        data_fixture):
    user = data_fixture.create_user()
    table = data_fixture.create_database_table(user=user)
    text_field = data_fixture.create_text_field(table=table,
                                                order=1,
                                                name="name")
    another_text_field = data_fixture.create_text_field(table=table,
                                                        order=2,
                                                        name="surname")

    model = table.get_model()
    row_1 = model.objects.create(
        **{
            f"field_{text_field.id}": "name",
            f"field_{another_text_field.id}": "other"
        })
    model.objects.create(
        **{
            f"field_{text_field.id}": "eman",
            f"field_{another_text_field.id}": "extra"
        })
    model.objects.create(
        **{
            f"field_{text_field.id}": "not_name",
            f"field_{another_text_field.id}": "not_other",
        })

    builder = FilterBuilder(filter_type=FILTER_TYPE_OR)
    builder.filter(
        AnnotatedQ(
            annotation={"reversed_name": Reverse(f"field_{text_field.id}")},
            q={f"field_{text_field.id}": "name"},
        ))
    builder.filter(Q(**{f"reversed_name": "eman"}))

    queryset = builder.apply_to_queryset(model.objects)
    assert queryset.count() == 1
    assert row_1 in queryset
Пример #7
0
def test_building_filter_with_annotated_qs_annotates_prior_to_filter(
        data_fixture):
    user = data_fixture.create_user()
    table = data_fixture.create_database_table(user=user)
    text_field = data_fixture.create_text_field(table=table,
                                                order=1,
                                                name='name')
    another_text_field = data_fixture.create_text_field(table=table,
                                                        order=2,
                                                        name='surname')

    model = table.get_model()
    row_1 = model.objects.create(
        **{
            f'field_{text_field.id}': 'name',
            f'field_{another_text_field.id}': 'other'
        })
    model.objects.create(
        **{
            f'field_{text_field.id}': 'eman',
            f'field_{another_text_field.id}': 'extra'
        })
    model.objects.create(
        **{
            f'field_{text_field.id}': 'not_name',
            f'field_{another_text_field.id}': 'not_other'
        })

    builder = FilterBuilder(filter_type=FILTER_TYPE_OR)
    builder.filter(
        AnnotatedQ(
            annotation={'reversed_name': Reverse(f'field_{text_field.id}')},
            q={f'field_{text_field.id}': 'name'}))
    builder.filter(Q(**{f'reversed_name': 'eman'}))

    queryset = builder.apply_to_queryset(model.objects)
    assert queryset.count() == 1
    assert row_1 in queryset
Пример #8
0
def test_building_filter_with_or_type_ors_all_provided_qs_together(
        data_fixture):
    user = data_fixture.create_user()
    table = data_fixture.create_database_table(user=user)
    text_field = data_fixture.create_text_field(table=table,
                                                order=1,
                                                name="name")
    another_text_field = data_fixture.create_text_field(table=table,
                                                        order=2,
                                                        name="surname")

    model = table.get_model()
    row_1 = model.objects.create(
        **{
            f"field_{text_field.id}": "name",
            f"field_{another_text_field.id}": "other"
        })
    row_2 = model.objects.create(
        **{
            f"field_{text_field.id}": "not_name",
            f"field_{another_text_field.id}": "extra",
        })
    model.objects.create(
        **{
            f"field_{text_field.id}": "not_name",
            f"field_{another_text_field.id}": "not_other",
        })

    builder = FilterBuilder(filter_type=FILTER_TYPE_OR)
    builder.filter(Q(**{f"field_{text_field.id}": "name"}))
    builder.filter(Q(**{f"field_{another_text_field.id}": "extra"}))

    queryset = builder.apply_to_queryset(model.objects)
    assert queryset.count() == 2
    assert row_1 in queryset
    assert row_2 in queryset
Пример #9
0
    def filter_by_fields_object(self, filter_object, filter_type=FILTER_TYPE_AND):
        """
        Filters the query by the provided filters in the filter_object. The following
        format `filter__field_{id}__{view_filter_type}` is expected as key and multiple
        values can be provided as a list containing strings. Only the view filter types
        are allowed.

        Example: {
            'filter__field_{id}__{view_filter_type}': {value}.
        }

        :param filter_object: The object containing the field and filter type as key
            and the filter value as value.
        :type filter_object: object
        :param filter_type: Indicates if the provided filters are in an AND or OR
            statement.
        :type filter_type: str
        :raises ValueError: Raised when the provided filer_type isn't AND or OR.
        :raises FilterFieldNotFound: Raised when the provided field isn't found in
            the model.
        :raises ViewFilterTypeDoesNotExist: when the view filter type doesn't exist.
        :raises ViewFilterTypeNotAllowedForField: when the view filter type isn't
            compatible with field type.
        :return: The filtered queryset.
        :rtype: QuerySet
        """

        if filter_type not in [FILTER_TYPE_AND, FILTER_TYPE_OR]:
            raise ValueError(f'Unknown filter type {filter_type}.')

        filter_builder = FilterBuilder(filter_type=filter_type)

        for key, values in filter_object.items():
            matches = deconstruct_filter_key_regex.match(key)

            if not matches:
                continue

            field_id = int(matches[1])

            if field_id not in self.model._field_objects:
                raise FilterFieldNotFound(
                    field_id, f'Field {field_id} does not exist.'
                )

            field_object = self.model._field_objects[field_id]
            field_name = field_object['name']
            field_type = field_object['type'].type
            model_field = self.model._meta.get_field(field_name)
            view_filter_type = view_filter_type_registry.get(matches[2])

            if field_type not in view_filter_type.compatible_field_types:
                raise ViewFilterTypeNotAllowedForField(
                    matches[2],
                    field_type,
                )

            if not isinstance(values, list):
                values = [values]

            for value in values:
                filter_builder.filter(
                    view_filter_type.get_filter(
                        field_name,
                        value,
                        model_field,
                        field_object['field']
                    )
                )

        return filter_builder.apply_to_queryset(self)