def test_field_not_provided(self, session):
        query = session.query(Bar)
        load_spec = [{}]

        with pytest.raises(BadLoadFormat) as err:
            apply_loads(query, load_spec)

        expected_error = '`fields` is a mandatory attribute.'
        assert expected_error == error_value(err)
    def test_wrong_spec_format(self, session, spec):
        query = session.query(Bar)
        load_spec = [spec]

        with pytest.raises(BadLoadFormat) as err:
            apply_loads(query, load_spec)

        expected_error = 'Load spec `{}` should be a dictionary.'.format(spec)
        assert expected_error == error_value(err)
    def test_invalid_field(self, session):
        query = session.query(Bar)
        load_spec = [{'fields': ['invalid_field']}]

        with pytest.raises(FieldNotFound) as err:
            apply_loads(query, load_spec)

        expected_error = (
            "Model <class 'test.models.Bar'> has no column `invalid_field`.")
        assert expected_error == error_value(err)
    def test_ambiguous_query(self, session):

        query = session.query(Foo, Bar)
        loads = [
            {'fields': ['count']},  # ambiguous
            {'model': 'Bar', 'fields': ['count']},
            {'model': 'Qux', 'fields': ['count']},
        ]

        with pytest.raises(BadSpec) as err:
            apply_loads(query, loads)

        assert 'Ambiguous spec. Please specify a model.' == err.value.args[0]
    def test_auto_join_to_invalid_model(self, session):

        query = session.query(Foo, Bar)
        loads = [
            {'model': 'Foo', 'fields': ['count']},
            {'model': 'Bar', 'fields': ['count']},
            {'model': 'Qux', 'fields': ['count']},
        ]

        with pytest.raises(BadSpec) as err:
            apply_loads(query, loads)

        assert 'The query does not contain model `Qux`.' == err.value.args[0]
    def test_eager_load(self, session, db_uri):

        query = session.query(Foo).options(joinedload(Foo.bar))
        load_spec = [
            {'model': 'Foo', 'fields': ['name']},
            {'model': 'Bar', 'fields': ['count']}
        ]
        restricted_query = apply_loads(query, load_spec)

        join_type = "INNER JOIN" if "mysql" in db_uri else "JOIN"

        # autojoin has no effect
        expected = (
            "SELECT "
            "foo.id AS foo_id, foo.name AS foo_name, "
            "foo.bar_id AS foo_bar_id, "
            "bar_1.id AS bar_1_id, bar_1.name AS bar_1_name, "
            "bar_1.count AS bar_1_count \n"
            "FROM foo {join} bar ON bar.id = foo.bar_id "
            "LEFT OUTER JOIN bar AS bar_1 ON bar_1.id = foo.bar_id".format(
                join=join_type
            )
        )

        assert str(restricted_query) == expected
    def test_no_load_provided(self, session):
        query = session.query(Bar)
        load_spec = []

        restricted_query = apply_loads(query, load_spec)

        # defers all fields
        expected = ("SELECT bar.id AS bar_id \n" "FROM bar")
        assert str(restricted_query) == expected
    def test_single_value(self, session):

        query = session.query(Bar)
        loads = [{'fields': ['name']}]

        restricted_query = apply_loads(query, loads)

        expected = ("SELECT bar.id AS bar_id, bar.name AS bar_name \n"
                    "FROM bar")
        assert str(restricted_query) == expected
    def test_multiple_values_single_model(self, session):

        query = session.query(Foo)
        loads = [{'fields': ['name', 'count']}]

        restricted_query = apply_loads(query, loads)

        expected = ("SELECT foo.id AS foo_id, foo.name AS foo_name, "
                    "foo.count AS foo_count \n"
                    "FROM foo")
        assert str(restricted_query) == expected
    def test_a_list_of_fields_can_be_supplied_as_load_spec(self, session):

        query = session.query(Foo)
        load_spec = ['name', 'count']

        restricted_query = apply_loads(query, load_spec)

        expected = ("SELECT foo.id AS foo_id, foo.name AS foo_name, "
                    "foo.count AS foo_count \n"
                    "FROM foo")
        assert str(restricted_query) == expected
    def test_multiple_values_multiple_models(self, session):

        query = session.query(Foo, Bar)
        loads = [
            {'model': 'Foo', 'fields': ['count']},
            {'model': 'Bar', 'fields': ['count']},
        ]

        restricted_query = apply_loads(query, loads)

        expected = (
            "SELECT foo.id AS foo_id, foo.count AS foo_count, "
            "bar.id AS bar_id, bar.count AS bar_count \n"
            "FROM foo, bar"
        )
        assert str(restricted_query) == expected
    def test_noop_if_query_contains_named_models(self, session, db_uri):

        query = session.query(Foo, Bar).join(Bar)
        loads = [
            {'model': 'Foo', 'fields': ['count']},
            {'model': 'Bar', 'fields': ['count']},
        ]

        restricted_query = apply_loads(query, loads)

        join_type = "INNER JOIN" if "mysql" in db_uri else "JOIN"

        expected = (
            "SELECT foo.id AS foo_id, foo.count AS foo_count, "
            "bar.id AS bar_id, bar.count AS bar_count \n"
            "FROM foo {join} bar ON bar.id = foo.bar_id".format(join=join_type)
        )
        assert str(restricted_query) == expected
    def test_auto_join(self, session, db_uri):

        query = session.query(Foo)
        loads = [
            {'fields': ['count']},
            {'model': 'Bar', 'fields': ['count']},
        ]

        restricted_query = apply_loads(query, loads)

        join_type = "INNER JOIN" if "mysql" in db_uri else "JOIN"

        # Bar is lazily joined, so the second loads directive has no effect
        expected = (
            "SELECT foo.id AS foo_id, foo.count AS foo_count \n"
            "FROM foo {join} bar ON bar.id = foo.bar_id".format(join=join_type)
        )
        assert str(restricted_query) == expected
Beispiel #14
0
    def dict2sqla(self, data):
        """

        :param data:
        :return:
        """
        invalid = []
        query = self._model.query
        fields = data.get('fields') or list(self._model.columns().keys())
        related = data.get('related') or {}
        filters = data.get('filters') or []
        sort = data.get('sorting') or []

        for k in fields:
            if k not in self._model.columns().keys():
                invalid.append(k)

        if len(invalid) == 0 and len(fields) > 0:
            try:
                query = sqlaf.apply_loads(query, fields)
            except exceptions.BadLoadFormat:
                invalid.append(fields)

        for k in related.keys():
            instance, columns = self._model.related(k)
            if instance is not None:
                _columns = related.get(k)
                try:
                    if len(_columns) > 0 and _columns[0] != self._syntax.ALL:
                        _invalid = list(set(related.get(k)) - set(columns))
                        if len(_invalid) > 0:
                            _columns = _invalid
                            raise ArgumentError
                    else:
                        _columns = columns

                    query = query.join(instance, aliased=False)
                    query = query.options(contains_eager(instance).load_only(*_columns))
                except ArgumentError:
                    invalid += _columns
            else:
                invalid.append(k)

        def apply(stm, flt, action):
            """

            :param stm:
            :param flt:
            :param action:
            :return:
            """
            resource = flt.get('model')
            try:
                if resource:
                    _, cols = self._model.related(resource)
                    if cols and cols.get(flt.get('field')) is None:
                        raise exceptions.FieldNotFound

                return action(stm, flt)
            except exceptions.BadSpec:
                invalid.append(resource)
            except exceptions.FieldNotFound:
                invalid.append(flt.get('field'))
            except exceptions.BadFilterFormat:
                invalid.append(flt.get('op'))
            except exceptions.BadSortFormat:
                invalid.append(flt.get('direction'))

        for f in filters:
            try:
                query = apply(query, f, sqlaf.apply_filters)
            except AttributeError:
                invalid.append(f)

        for s in sort:
            try:
                query = apply(query, s, sqlaf.apply_sort)
            except AttributeError:
                invalid.append(s)

        return query, invalid