Git Product home page Git Product logo

graphene-sqlalchemy-filter's People

Contributors

art1415926535 avatar davidcim avatar j-a-n avatar maquino1985 avatar nyejon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphene-sqlalchemy-filter's Issues

Support for `hybrid_property` fields?

I tried using the FilterSet Class to create a filter on a field defined by a hybrid_property in my sqlalchemy model, but it seems to have no effect. Graphene-Sqlalchemy does resolve the field on the type, but the filters argument does not accept the hybrid_property field I defined. I tried both the shortcut [...] as well as explicit filter types but neither worked. Is this supported?

Here's an example (overly simplified for clarity):

models.py

class Post(db.Model):
    __tablename__ = "projects"

    id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)

    @hybrid_property
    def status(self):
        return "Submitted"

filters.py

class PostFilter(FilterSet):
    class Meta:
        model = Post
        fields = {
            'status': ["eq", "in"]
        }

class CustomFilter(FilterableConnectionField):
    filters = {
        Post: PostFilter(),
    }

schema.py

class Post(SQLAlchemyObjectType):
    class Meta:
        model = PostModel
        interfaces = (Node,)
        connection_field_factory = CustomFilter.factory
        connection_class = CountableConnection

Running this query:

query {
  allPosts (filters: {status: "Submitted"}) {
    edges {
      node {
        status
      }
    }
  }
}

Returns:

{
  "errors": [
    {
      "message": "Argument \"filters\" has invalid value {status: \"Submitted\"}.\nIn field \"status\": Unknown field.",
      "locations": [
        {
          "line": 2,
          "column": 25
        }
      ]
    }
  ]
}

Support for field synonyms

I have a requirement to expose the actual id of the table back to the user. By default, id will be converted to graphql node id and the only way around is to create a synonym something like this in the model definition.

from sqlalchemy.orm import synonym
.
.
.
class SomeModel(Base):
     __tablename__ = 'changelog'
     id = Column(BigInteger, primary_key=True)
     db_id = synonym('id)

Looks like that is not supported in graphene-sqllachemy-filter yet. (I'm using FilterableConnectionField)

Pass dictionary as a query parameter

Is it possible to pass a filter as a query parameter?
That is defining something like:
query($filters: Filter!){ ... }
And pass a dictionary to it (a.k.a. assign it to $filters) ? What type would Filter be?

can not have a field named `type` with `in` filter

I have a field named type and adding that in filterset fields give the following error to when creating the schema.

Exception has occurred: AttributeError
type object 'function' has no attribute 'name'

eg:

class SampleFilter(FilterSet):
    class Meta:
        model = SampleModel
        fields = {
            'name': [...],
            'type': [...],
        }

class Query(graphene.ObjectType):
   node = graphene.relay.Node.Field()
   all_samples = FilterableConnectionField(SampleSchemaObject.connection, filters=SampleFilter())

schema = graphene.Schema(query=Query)

Auto define all filters for all fields via Ellipsis

It would be very helpful if we could do something like this, in order to automatically define filters for all fields and all operators:

class UserFilter(BaseFilter):
    class Meta:
        model = User
        fields = [...]

Are there already opinions or solutions for this that I missed?

Is it possible to filter in both directions?

Is it possible to do something like the following, which allows filtering of addresses by user and filtering of users by their addresses. This code doesn't work because at the time the line address = AddressFilter() is executed, the class AddressFilter hasn't been defined.

class UserFilter(graphene_sqlalchemy_filter.FilterSet):

    address = AddressFilter()

    @classmethod
    def address_filter(cls, info, query, value:

        return cls.address.filter(info, query, value).join(db.Address), None

    class Meta:

        model = db.User
        fields = {"text": [...], "strong": ["eq", "ne"]}
class AddressFilter(graphene_sqlalchemy_filter.FilterSet):

    user = UserFilter()

    @classmethod
    def user_filter(cls, info, query, value):

        return cls.user.filter(info, query, value).join(db.User), None

    class Meta:

        model = db.Address
        fields = {"text": [...], "strong": [...],}

Maybe this is impossible because it would cause an infinite loop somewhere.

I've also tried adding UserFilter.address = AddressFilter() at the bottom of the file, but although it doesn't throw any errors, it also doesn't work.

Filters without manual filters= option

Hey Artem,

In issue #6 we also discussed that your syntax for creating FilterableConnectionFields requires argument filters=:

class ModelAFilter(FilterSet):
  # ...
class ModelA(ObjectType):
  Meta:
    model = ModelAModel
    # ...

class RootQuery(graphene.ObjectType):
  something = FilterableConnectionField('ModelA', filters=ModelAFilter()) # <--- 

Could you somehow make it so that the ModelAFilter is specified directly inside ModelA or it's meta class, so that one does not have to manually pass the filters= option when creating a field?

Thanks!

How to make a filter field required?

Since the dataset is very large in my case, I want to have a feature to limit the size of a response dataset. For example, I want to make a filter field like timestamp required. Is this a correct way?

Thanks for your great works!

Nested fields filters on self referenced entities causing an SQLAlchemy error

First of all thanks for the awesome library! :)

I have a system with this sort of hierarchy:

  • Chapter
    -- Chapter
    --- Checklist
    --- Checklist
  • Chapter
    -- Checklist

Chapter.py

class Chapter(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String, nullable=False)
    chapter_id = db.Column(ForeignKey('go_tab_chapters.id'))
    sub_chapters = relationship('Chapter', backref=backref('chapter', remote_side=[id]),
                                primaryjoin=id == chapter_id)
    checklists = relationship(Checklist, backref=backref('chapter'), uselist=True)

Checklist.py

class Checklist(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String, nullable=False)
    chapter_id = db.Column(ForeignKey('go_tab_chapters.id'))

This is my GraphQL entities:

class ChapterFilter(FilterSet):
    class Meta:
        model = Chapter
        fields = {'name': [...]}

class ChecklistFilter(FilterSet):
    class Meta:
        model = Checklist
        fields = {
            'name': [...]
        }

class CustomField(FilterableConnectionField):
    filters = {
        Chapter: ChapterFilter(),
        Checklist: ChecklistFilter()
    }

class ChapterNode(SQLAlchemyObjectType):
    class Meta:
        model = Chapter
        interfaces = (graphene.relay.Node,)
        connection_field_factory = CustomField.factory

class ChapterConnection(Connection):
    class Meta:
        node = ChapterNode

class ChecklistNode(SQLAlchemyObjectType):
    class Meta:
        model = Checklist
        interfaces = (graphene.relay.Node,)
        connection_field_factory = CustomField.factory

class ChecklistConnection(Connection):
    class Meta:
        node = ChecklistNode

When I'm trying to run the following query:

{
  chapters (filters:{
    name: "abc"
  }){
    edges{
      node{
        name
        checklists (filters:{
          name: "abc"
        }){
          edges{
            node{
              name
            }
          }
        }
        subChapters{
          edges{
            node{
              name
            }
          }
        }
      }
    }
  }
}

I get the following error:
graphql.error.located_error.GraphQLLocatedError: Can't construct a join from Mapper|Chapter|chapters to Mapper|Chapter|chapters, they are the same entity

When I comment the following line the query works but not the nested filter:

class ChapterNode(SQLAlchemyObjectType):
    class Meta:
        model = Chapter
        interfaces = (graphene.relay.Node,)
        # connection_field_factory = CustomField.factory

@art1415926535 , Can you please help?

Many thanks!

AND / OR doesn't work with repeating clauses

Something like this doesn't work as you would expect, only the last and clause is taken:

{
  cuentas(first: 10, filters: {and: {cuentaIlike: "%co%" cuentaIlike: "%pa%"}}) {
    edges {
      node {
        idCuenta
        cuenta
      }
    }
  }
}

You can get it to work with this hack:

{
  cuentas(first: 10, filters: {and: {cuentaIlike: "%co%" and: {cuentaIlike: "%pa%"}}}) {
    edges {
      node {
        idCuenta
        cuenta
      }
    }
  }
}

Optimizing MySQL queries

More a question than an issue.

I'm noticing that rendered MySQL queries are sometimes un-optimized, e.g.

SELECT table_1.id AS table_1_id, anon_1.id AS anon_1_id, anon_1.first_name AS anon_1_first_name, anon_1.last_name AS anon_1_last_name, anon_1.email AS anon_1_email 
FROM table_1 INNER JOIN (SELECT cglookup_persons.id AS id, cglookup_persons.first_name AS first_name, cglookup_persons.last_name AS last_name, cglookup_persons.email AS email 
FROM cglookup_persons ORDER BY cglookup_persons.id ASC) AS anon_1 ON anon_1.id = table_1.person 
WHERE table_1.id IN (2199927)

the nested query causes the entire table to be loaded into a hash table to be first sorted only then to be completely excluded by the join.

Skipping the ORDER BY would be enough to greatly reduce the execution time, something like

SELECT table_1.id AS table_1_id, anon_1.id AS anon_1_id, anon_1.first_name AS anon_1_first_name, anon_1.last_name AS anon_1_last_name, anon_1.email AS anon_1_email 
FROM table_1 INNER JOIN cglookup_persons AS anon_1 ON anon_1.id = table_1.person 
WHERE table_1.id IN (2199927)

is this achievable in any way? I guess is related to the sorting, so I'm going to try that, though I was wondering if you have some experience with it to avoid side effects.

sorting on nested fields not working

Hi,

I followed the instructions to set up the nested filters. They seem to work, but the sorting does not.

Are there additional steps required to get the sorting to work?

Thanks!

Jonathan

Filter options on all connections

Hello,

The module is great. I have it running successfully when fields are defined at the top of the tree, like you show in the example:

class Query(ObjectType):
    all_users = CustomField(UserConnection, where=UserFilter())

However, graphene-sqlalchemy automatically finds and wraps all relationships between ORM models into GraphQL connections.

How did you envision that the benefits of this module could be applied to all those connections automatically?

Thanks.

AttributeError: columns

Seeing the following error after updating package version from 1.11.1 to 1.12.0:

my_code/graphql/filters/specimen.py:7: in <module>
    class SpecimenFilter(FilterSet):
.tox/py38/lib/python3.8/site-packages/graphene/utils/subclass_with_meta.py:52: in __init_subclass__
    super_class.__init_subclass_with_meta__(**options)
.tox/py38/lib/python3.8/site-packages/graphene_sqlalchemy_filter/filters.py:292: in __init_subclass_with_meta__
    filters_fields = cls._generate_default_filters(model, fields)
.tox/py38/lib/python3.8/site-packages/graphene_sqlalchemy_filter/filters.py:511: in _generate_default_filters
    model_fields = cls._get_model_fields_data(model, field_filters.keys())
.tox/py38/lib/python3.8/site-packages/graphene_sqlalchemy_filter/filters.py:609: in _get_model_fields_data
    column = attr.columns[0]
.tox/py38/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py:977: in __getattr__
    return self._fallback_getattr(key)
.tox/py38/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py:951: in _fallback_getattr
    raise AttributeError(key)
E   AttributeError: columns

specimen.py looks like:

class SpecimenFilter(FilterSet):
    class Meta:
        model = SpecimenModel
        fields = {
            "type": ["eq", "ne", "in", "not_in"],
            "status": ["eq", "ne"],
            "group": ["eq"],
            "identifier": ["eq", "ne", "like", "ilike", "contains"],
            "created_at": ["gt", "lt", "gte", "lte"],
            "updated_at": ["gt", "lt", "gte", "lte"],
        }

    is_ready = graphene.Boolean()

    @staticmethod
    def is_ready_filter(info, query, value):
        ready = SpecimenModel.is_ready  # this is a hybrid_property that performs some joins
        return ready if value else not ready

It appears this commit introduces the issue: 2562ad7

I am still investigating. Wanted to post the issue early in case this is a regression from the most-recent release. I will update with more when/if I gain more insight.

Did I miss a breaking API change or other deprecation?
Thanks!

How to use without Relay?

Hey thank you for this great lib and it solves a lot of my problems.
In my use case, I don't really need Relay and the Relay kinda makes the query confusing as we need to add edges and node etc.
I'd like to keep the normal GraphQL query syntax but also have the auto-generated filters.
Is there a way to accomplish this? Do we have a FilterableField instead of FilterableConnectionField?
Thank you!

How to filter results

Hi, is there a way to do custom filtering on the results? I want to filter out results that the user doesn't not have access to so it can't be part of the initial query.

Wrapping of _id fields

Let's say we have 2 models with a relationship between them, such as:

class ModelA:
  modelb_id = db.Column(db.BigInteger(), db.ForeignKey('modelb.id'), nullable=True)
  modelb = db.relationship('ModelB')

class ModelB:
  name = db.Column(db.Text, ...)
  ...

Now, when the filter module wraps ModelA, it will automatically make the following available through GraphQL:

modela(...) {
  modelbId
  modelb {
    id
    name
  }
}

However, the value in modelbId will be a plain integer, rather than a global/hashed GraphQL ID like obtained when querying modelb { id }.

Based on that, I have two questions:

  1. Is it possible to add built-in support so that all the cases like this are automatically recognized and return GraphQL IDs?

  2. If it is not possible or not accepted to add this as built-in behavior to the filter module, how would one do this manually in their project?

Thanks!

Postgres list contains value

Maybe I am missing something obvious, but how would I go about writing a contains filter for postgres?

This is the class method because I am modifying the query.

@classmethod
def in_old_ids_filter(cls, info, query, value):
    has_id = cls.aliased(query, Model, name='in_old_ids')

   query = query.filter(Model.old_ids.contains(value))  # postgres sqlalchemy

   return query, ???

Thanks!

'Request' object has no attribute 'id'

In my models, I renamed the column id to be request_id

class Request(Base):

    __tablename__ = "requests"

    request_id = Column("id", String, primary_key=True)

    datasets = relationship("Dataset")

The filter gave me this error "message": "'Request' object has no attribute 'id'", when I queried datasets. But if I used id=Column(String, primary_key=True), it worked. Any idea?

Support for MS SQL types

Hi, would it be possible to include mssql types as well as potsgreSQL?When my data is in MS SQL database it throws an error using declerativebase in SQL alchemy if I try to make a filter on variable that isn't converted into SQLalchemy type. Here is the error:

File "\venv\lib\site-packages\graphene_sqlalchemy_filter\filters.py", line 359, in _generate_default_filters expressions = filters_map[column_type.__class__].copy() KeyError: <class 'sqlalchemy.sql.sqltypes.NVARCHAR'>

Custom filter with SQLAlchemy relationship

Is it possible to create a filter using properties from a relationship to a model? Say for the models:

from database import Base
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, backref
class Foo(Base):
    __tablename__ = 'foo'

    f_id = Column(Integer, primary_key = True)
    name = Column(String)
    bar = relationship('Bar', backref='foo')

class Bar(Base):
    __tablename__ = 'bar'

    b_id = Column(Integer, primary_key = True)
    number = Column(Int)
    f_id = Column(Integer, ForeignKey('foo.f_id'))

I would like to create a filter that might emulate the following:

from graphene import Int
from graphene_sqlalchemy_filter import FilterSet

class FooFilter(FilterSet):
    number = Int()
    class Meta:
            model = Foo
    def number_filter(self, query, value):
            return Foo.Bar.number == value

This doesn't work, but how would I go about to get similar functionality? Any help appreciated!

Support for VARBINARY Fields for Unicode

How should I configure graphene-sqlalchemy-filter to account for VARBINARY fields in a SQLAlchemy model?

I have no issues using graphene-sqlalchemy-filter filtering on INTEGER columns. With VARBINARY defined columns, I can filter using numeric only strings with these caveats.

  1. Filtering with a "0" gives every value.
  2. Filtering with an "AlphaNumeric" value or a a numeric value with a leading zero gives this error. The character referenced is the location of the first non-numeric character in the string. In this case the filter string was "75556b"
{
  "errors": [
    {
      "message": "Extra data: line 1 column 6 (char 5)"
    }
  ]
}

You can view my code here where I'm attempting to filter the PairXlate table on key (VARBINARY) and idx_pair_xlate_group (BIGINT).
: https://github.com/palisadoes/pattoo/tree/753885eb4dfa72c525425a29c98e5fd6ef6fb8ff/pattoo/db

Add implicit filter

The filtration library seems very close to what's needed for authorization logic. We have an incoming JWT token and would like to do something like adding 'implicit' filters (sometimes with joins) on most objects based on whether the userid (or, for joins, the userid in a related object) is the same as what's in the JWT.

Is there a good way to write filters and then have them triggered even when the graphql query did not include a filter, i.e., to mark certain kinds of filters as 'implicit' and use those for authorization?

I'm new to both the library and graphql. I've looked around a bit here & elsewhere and seen a bit of discussion of auth, but so far I can't figure out whether this is a reasonable direction to head.

NoneType related warnings / errors when using schema.execute()

In some cases when calling a query directly with schema.execute(query_string), I'll get some warning / error message about NoneType. However, when using a flask app instead, I don't receive any error messages on the server side nor the client side.

Full test case

from sqlalchemy import create_engine, Table, Column, Integer, String, ForeignKey
from sqlalchemy.orm import scoped_session, sessionmaker, relationship, backref
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine("sqlite:///:memory:", echo=True)
db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()

class Song(Base):
    __tablename__ = 'songs'
    song_id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    tags = relationship('Tag',
                        secondary='song_tag_table',
                        backref='songs')

class Tag(Base):
    __tablename__ = 'tags'
    tag_id = Column(Integer, primary_key=True)
    name = Column(String)

class SongTagTable(Base):
    __tablename__ = 'song_tag_table'
    id = Column(Integer, primary_key=True)
    song_id = Column(Integer, ForeignKey('songs.song_id'))
    tag_id = Column(Integer, ForeignKey('tags.tag_id'))

Base.metadata.create_all(engine)

# ==========

from graphene import ObjectType, Schema
from graphene.relay import Connection, Node
from graphene_sqlalchemy import SQLAlchemyObjectType
from graphene_sqlalchemy_filter import FilterableConnectionField, FilterSet

class SongFilter(FilterSet):
    class Meta:
        model = Song
        fields = {'song_id': ['eq', 'ne', 'in']}

class TagFilter(FilterSet):
    class Meta:
        model = Tag
        fields = {'tag_id': ['eq', 'ne', 'in']}

class CustomField(FilterableConnectionField):
    filters = {
        Song: SongFilter(),
        Tag: TagFilter()
    }

class SongNode(SQLAlchemyObjectType):
    class Meta:
        model = Song
        interfaces = (Node,)
        connection_field_factory = CustomField.factory

class TagNode(SQLAlchemyObjectType):
    class Meta:
        model = Tag
        interfaces = (Node,)
        connection_field_factory = CustomField.factory

class SongConnection(Connection):
    class Meta:
        node = SongNode

class TagConnection(Connection):
    class Meta:
        node = TagNode

class Query(ObjectType):
    all_songs = CustomField(SongConnection)
    all_tags = CustomField(TagConnection)

schema = Schema(query=Query)

# ==========

jpop = Tag(name='jpop')

subarashi = Song(name='素晴らしい日々')
subarashi.tags.append(jpop)
db_session.add(subarashi)

fool_in_tank = Song(name='水槽のフール')
fool_in_tank.tags.append(jpop)
db_session.add(fool_in_tank)

despacito = Song(name='despacito')
db_session.add(despacito)
db_session.commit()

# ==========

query_string = """
insert test query here
"""
res = schema.execute(query_string)
print(res)

Normal test case, no errors

query_string = """
query {
  allSongs {
    edges {
      node {
        name
      }
    }
  }
}
"""

response (normal):

{'data': {'allSongs': {'edges': [{'node': {'name': '素晴らしい日々'}}, {'node': {'name': '水槽のフール'}}, {'node': {'name': 'despacito'}}]}}}

Testing filtering, runtime warning

query_string = """
query {
  allSongs(filters: {songIdNe: 2}) {
    edges {
      node {
        name
      }
    }
  }
}
"""

response (normal):

{'data': {'allSongs': {'edges': [{'node': {'name': '素晴らしい日々'}}, {'node': {'name': 'despacito'}}]}}}

warning:

c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\filters.py:750: RuntimeWarning: Graphene-SQLAlchemy-Filter: info.context has an unsupported type <class 'NoneType'>. Now cls.aliased(info, ...) is not supported. Allowed types: dict and object with __dict__ attribute.
  warnings.warn(msg, RuntimeWarning)

Large error

query_string = """
query {
  allSongs {
    edges {
      node {
        name
        tags {
          edges {
            node {
              name
            }
          }
        }
      }
    }
  }
}
"""

response:

{'errors': [{'message': "'NoneType' object has no attribute '_sqla_filter_dataloaders'", 'locations': [{'line': 7, 'column': 9}], 'path': ['allSongs', 'edges', 0, 'node', 'tags']}, {'message': "'NoneType' object has no attribute '_sqla_filter_dataloaders'", 'locations': [{'line': 7, 'column': 9}], 'path': ['allSongs', 'edges', 1, 'node', 'tags']}, {'message': "'NoneType' object has no attribute '_sqla_filter_dataloaders'", 'locations': [{'line': 7, 'column': 9}], 'path': ['allSongs', 'edges', 2, 'node', 'tags']}], 'data': {'allSongs': {'edges': [{'node': {'name': '素晴らしい日々', 'tags': None}}, {'node': {'name': '水槽のフール', 'tags': None}}, {'node': {'name': 'despacito', 'tags': None}}]}}}

error:

An error occurred while resolving field SongNode.tags
Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executor.py", line 452, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executors\sync.py", line 16, in execute
    return fn(*args, **kwargs)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 356, in connection_resolver
    data_loader: ModelLoader = cls._get_or_create_data_loader(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 318, in _get_or_create_data_loader
    setattr(info.context, cls.dataloaders_field, data_loaders)
AttributeError: 'NoneType' object has no attribute '_sqla_filter_dataloaders'
Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executor.py", line 452, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executors\sync.py", line 16, in execute
    return fn(*args, **kwargs)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 356, in connection_resolver
    data_loader: ModelLoader = cls._get_or_create_data_loader(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 318, in _get_or_create_data_loader
    setattr(info.context, cls.dataloaders_field, data_loaders)
graphql.error.located_error.GraphQLLocatedError: 'NoneType' object has no attribute '_sqla_filter_dataloaders'

An error occurred while resolving field SongNode.tags
Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executor.py", line 452, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executors\sync.py", line 16, in execute
    return fn(*args, **kwargs)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 356, in connection_resolver
    data_loader: ModelLoader = cls._get_or_create_data_loader(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 318, in _get_or_create_data_loader
    setattr(info.context, cls.dataloaders_field, data_loaders)
AttributeError: 'NoneType' object has no attribute '_sqla_filter_dataloaders'
Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executor.py", line 452, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executors\sync.py", line 16, in execute
    return fn(*args, **kwargs)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 356, in connection_resolver
    data_loader: ModelLoader = cls._get_or_create_data_loader(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 318, in _get_or_create_data_loader
    setattr(info.context, cls.dataloaders_field, data_loaders)
graphql.error.located_error.GraphQLLocatedError: 'NoneType' object has no attribute '_sqla_filter_dataloaders'

An error occurred while resolving field SongNode.tags
Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executor.py", line 452, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executors\sync.py", line 16, in execute
    return fn(*args, **kwargs)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 356, in connection_resolver
    data_loader: ModelLoader = cls._get_or_create_data_loader(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 318, in _get_or_create_data_loader
    setattr(info.context, cls.dataloaders_field, data_loaders)
AttributeError: 'NoneType' object has no attribute '_sqla_filter_dataloaders'
Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executor.py", line 452, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executors\sync.py", line 16, in execute
    return fn(*args, **kwargs)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 356, in connection_resolver
    data_loader: ModelLoader = cls._get_or_create_data_loader(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 318, in _get_or_create_data_loader
    setattr(info.context, cls.dataloaders_field, data_loaders)
graphql.error.located_error.GraphQLLocatedError: 'NoneType' object has no attribute '_sqla_filter_dataloaders'

library versions:

python: 3.8.0
sqlalchemy: 1.3.20
graphene: 2.1.8
graphene_sqlalchemy: 2.3.0
graphene_sqlalchemy_filter: 1.12.1

I'm new to both sqlalchemy and graphql, so hopefully I didn't make any trivial mistakes. Thanks!

totalCount() via database `count()`?

Hey,

I have a query like this:

      someObjects(filters: {and: [{status: "DONE"}]}) {
        totalCount
      }

This is producing totalCount by retrieving all this in Python and doing a len() on it.

What's the suggested way to make this be a DB query using count?
(If the only solution is to rewrite the whole query in a different way or to provide my own/custom field which does count(), that's a fine too.)

Problem with Enum field

Hi

I'm trying to use Enum in filter but get a problem

models

class IssueCommentType(Enum):
    default = 'Not defined'
    author = 'Response from author'

class IssueComment(db.Model):
    comment_id = db.Column(db.INT, primary_key=True)
    issue_id = db.Column(db.ForeignKey('issue.issue_id'), nullable=False)

    comment = db.Column(db.TEXT, nullable=False)
    date = db.Column(db.DateTime, nullable=False)

    type = db.Column(db.Enum(IssueCommentType), nullable=False, default=IssueCommentType.default)

filter

class IssueCommentFilter(FilterSet):
    class Meta:
        model = IssueComment
        fields = {
            'type': ['eq', 'ne'],
        }

class MyFilterableConnectionField(FilterableConnectionField):
    filters = {
        Issue: IssueFilter(),
        IssueComment: IssueCommentFilter(),
        IssueExtraFields: ExtraFieldsFilter(),
    }

query

{
  issues(first: 10) {
    totalCount
    edges {
      node {
        comments(filters: {type:AUTHOR}) {
          edges {
            node {
              comment
              date
              type
            }
          }
        }
      }
    }
  }
}

error

graphql.error.located_error.GraphQLLocatedError: (psycopg2.errors.InvalidTextRepresentation) 
ERROR:  incorrect value for enum issuecommenttype: "Response from author"
LINE 4: WHERE issue_comment.type = 'Response from author') AS anon_1...
                                   ^

enum in database created with enum names but filter trying to use enum values

'Request' object has no attribute 'get'

Hi,
I am getting this error with FilterableConnectionField, however with original SQLAlchemyConnectionField it works. I have tried with my own model as well as with dummy User model used in the example. Using Flask.

An error occurred while resolving field Query.allPreds Traceback (most recent call last): File "/home/datas/predictions-api/venv/lib/python3.6/site-packages/graphql/execution/executor.py", line 450, in resolve_or_error return executor.execute(resolve_fn, source, info, **args) File "/home/datas/predictions-api/venv/lib/python3.6/site-packages/graphql/execution/executors/sync.py", line 16, in execute return fn(*args, **kwargs) File "/home/datas/predictions-api/venv/lib/python3.6/site-packages/graphene_sqlalchemy/fields.py", line 74, in connection_resolver return on_resolve(resolved) File "/home/datas/predictions-api/venv/lib/python3.6/site-packages/graphene_sqlalchemy/fields.py", line 47, in resolve_connection resolved = cls.get_query(model, info, **args) File "/home/datas/predictions-api/venv/lib/python3.6/site-packages/graphene_sqlalchemy_filter/connection_field.py", line 32, in get_query query = filter_set.filter(info, query, request_filters) File "/home/datas/predictions-api/venv/lib/python3.6/site-packages/graphene_sqlalchemy_filter/filters.py", line 446, in filter info.context[cls._filter_aliases] = {} File "/home/datas/predictions-api/venv/lib/python3.6/site-packages/werkzeug/local.py", line 351, in __setitem__ self._get_current_object()[key] = value TypeError: 'Request' object does not support item assignment Traceback (most recent call last): File "/home/datas/predictions-api/venv/lib/python3.6/site-packages/graphql/execution/executor.py", line 450, in resolve_or_error return executor.execute(resolve_fn, source, info, **args) File "/home/datas/predictions-api/venv/lib/python3.6/site-packages/graphql/execution/executors/sync.py", line 16, in execute return fn(*args, **kwargs) File "/home/datas/predictions-api/venv/lib/python3.6/site-packages/graphene_sqlalchemy/fields.py", line 74, in connection_resolver return on_resolve(resolved) File "/home/datas/predictions-api/venv/lib/python3.6/site-packages/graphene_sqlalchemy/fields.py", line 47, in resolve_connection resolved = cls.get_query(model, info, **args) File "/home/datas/predictions-api/venv/lib/python3.6/site-packages/graphene_sqlalchemy_filter/connection_field.py", line 32, in get_query query = filter_set.filter(info, query, request_filters) File "/home/datas/predictions-api/venv/lib/python3.6/site-packages/graphene_sqlalchemy_filter/filters.py", line 446, in filter info.context[cls._filter_aliases] = {} File "/home/datas/predictions-api/venv/lib/python3.6/site-packages/werkzeug/local.py", line 351, in __setitem__ self._get_current_object()[key] = value graphql.error.located_error.GraphQLLocatedError: 'Request' object does not support item assignment

query
{ allPreds (filters:{shopId: 134}) { edges { node { id } } } }

Enum field not working

Hi

I am trying to filter on an Enum field, but if I add one to the filter I get:

File "/opt/venv/lib/python3.8/site-packages/graphene_sqlalchemy_filter/filters.py", line 531, in _generate_default_filters
fields = cls._generate_filter_fields(
File "/opt/venv/lib/python3.8/site-packages/graphene_sqlalchemy_filter/filters.py", line 610, in _generate_filter_fields
filter_field = field_type(description=doc)
TypeError: function() missing required argument 'code' (pos 1)

Do you have any ideas how to fix this?

Nested fields filters example does not work

{
  allUsers(filters: {isActive: 1}) {
    edges {
      node {
        id
      }
    }
  }
  allGroups {
    edges {
      node {
        users(filters: {isActive: 1}) {
          edges {
            node {
              name
            }
          }
        }
      }
    }
  }
}

model:

class TmTeamsUsers(db.Model):
    """Model for 'tmteamsusers'"""

    __tablename__ = 'tmteamsusers'
    teamid = db.Column('teamid', INTEGER(11), primary_key=True, nullable=False)

    userid = db.Column('userid', INTEGER(11), primary_key=True, nullable=False)

    minimizeWindow = db.Column('minimizeWindow', SMALLINT(1), nullable=False)

    isAdministrator = db.Column('isAdministrator', SMALLINT(1), nullable=False)

    intranetid = db.Column('intranetid', INTEGER(11), nullable=False)

    isActive = db.Column('isActive', SMALLINT(1, unsigned=True), nullable=False)

error:

{
  "errors": [
    {
      "message": "Cannot query field \"users\" on type \"GroupNode\". Did you mean \"userid\"?",
      "locations": [
        {
          "line": 12,
          "column": 9
        }
      ]
    }
  ]
}

What do I have to add to my model in order to get this working?

    user = relationship('User', foreign_keys=[userid], primaryjoin='User.id == TmTeamsUsers.userid')

Does not work for example.

Nested Graphql-SQLAlchemy sorting broken for association tables

Howdy,
First things first thank you for the amazing library.
It seems that the model loader's get query method incorrectly adds order_by arguments to the generated sqlalchemy query. Order by gets added here to the subquery which makes sense because they are relevant to the table that subquery deals with. However in cases where an association table is used (have not checked for typical relationships) the order by is not respected because the subquery result is joined again back to parent table.

I was able to solve the problem by patching the aforementioned function and adding a section that translates the order by arguments to the aliased model so that I could add them outside the subquery:

...

# Translate sortEnums of the original model to the aliased one
    aliased_sort = []
    for col in sort:
        # Use direction present in the original name to pick a SQLAlchemy direction
        direction_func = asc if col.lower().endswith("asc") else desc
        # Get everything left of the first "_"
        name = "_".join(col.lower().split("_")[:-1])
        # Derive the aliased column from the extracted name of the original
        alias_column = getattr(aliased_model, name)
        # Combine
        aliased_sortable = direction_func(alias_column)
        aliased_sort.append(aliased_sortable)

    query = (
        get_query(self.parent_model, self.info.context)
        .join(aliased_model, self.relation)
        .options(
            contains_eager(self.relation, alias=aliased_model),
            Load(self.parent_model).load_only(self.parent_model_pk_field),
        )
        .order_by(
            *(col for col in aliased_sort)
        )  # Add an ordering by the alias table outside the join so order is respected
    )
    return query
...

I recognize this to be a bit on the hacky side and I haven't tested it extensively but I'm interested in feedback. I'd be happy to clean it up and submit a PR to fix if interested.

[Clarification] Pagination performed by python?

Hello there.

Thanks for creating this library, it is helping me a lot in my current project. I saw no other direct way to contact you so I decided to post this as an issue, sorry if this is inappropriate.

I noticed in the readme under features the following line:

pagination (first/after, last/before) are performed by python (keep this in mind when working with large amounts of data)

First I was discouraged as I will most likely be working on quite large amounts of data at some point in the future (potentially millions of rows). I looked through the repo trying to find where pagination is handled but was not able to. After some more digging, I enabled debug logging for sqlalchemy and noticed that limit and offset is sent to the database.

SELECT auth."user".id AS auth_user_id, auth."user".email AS auth_user_email, auth."user".password AS auth_user_password, auth."user".lastmodified AS auth_user_lastmodified, auth."user"."roleId" AS "auth_user_roleId" 
FROM auth."user" ORDER BY auth."user".lastmodified DESC 
 LIMIT %(param_1)s OFFSET %(param_2)s

Does your original statement still hold true or is this graphene actually handling pagination via postgres? Thanks once again.

Ignore `null` filter parameters for eq filter

If I have a query written like:

query myQuery ($myFieldFilter: ID) {
  myType (filters: {myField: $myFieldFilter}) {
    myField
}

If I pass the parameter $myFieldFilter as null, I would like to essentially ignore the filtering entirely. This would allow me on the front end to not have to modify the structure of the query itself. It seems there is already a way to explicitly find null values with the filter type: myFieldIsNull. I was able to achieve this by modifying the function:

def _eq_filter(field: 'Column', value: 'Any') -> 'Any':
    if value is None:
        return True  # ignore filter

    column_type = field.type
    if isinstance(column_type, postgresql.ARRAY):
        value = cast(value, column_type)

    return field == value

This is perhaps not the desired behavior of the filter in general so perhaps a setting in the Meta class to toggle the functionality would be desirable. Not sure if this is functionality other people would find useful, just wanted to share my experience.

FilterSet.aliased no longer works with SQLAlchemy 1.4

membership = cls.aliased(query, Membership, name='is_moderator')
GraphQLLocatedError: 'Query' object has no attribute '_join_entities'

Since SQLALchemy 1.4, Query._join_entities method doesn't exist any more. This method is used in the implementation of FilterSet._aliases_from_query.

When I try to define a filter using a join, following the instructions in the documentation, the exception above is thrown.

Error for nested filtered connection fields that are Sets

The UnsortedSQLAlchemyConnectionField.resolve_connection function throws the following error when it tries to resolve relationship fields that are Sets instead of lists (defined using SQLAlchemy's "collection_class=set" on the relationship).

ERROR:    Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/promise/promise.py", line 87, in try_catch
    return (handler(*args, **kwargs), None)
  File "/usr/local/lib/python3.8/site-packages/graphene_sqlalchemy/fields.py", line 56, in resolve_connection
    connection = connection_from_list_slice(
  File "/usr/local/lib/python3.8/site-packages/graphql_relay/connection/arrayconnection.py", line 79, in connection_from_list_slice
    _slice = list_slice[
graphql.error.located_error.GraphQLLocatedError: 'InstrumentedSet' object is not subscriptable

This can be worked around for fields in the GraphQL model classes by adding a resolver function that converts the sets to lists, but nested filtered connection fields use the UnsortedSQLAlchemyConnectionField.resolve_connection function by default, which doesn't seem to be easy to override.

Wrong results returned?

So I have 2 records in schema Foo:
id: 1, key: 'foo'
id: 2, key: 'foobar'

if I run the below command:
query { Foo(filters: { key_like: "%foo%", and: [{ id: "1" }] }) { id key } }
it will return:
{ "data": { "Foo": [ { "id": "1", "key": "foo" } ] } }
, which is expected, as both records' key like foo, but only record 1 has the id 1, so only the record 1 was returned;

but if I run the below command:

query { Foo(filters: { key_like: "%foo%", or: [{ id: "2" }] }) { id key } }
it will ONLY return:
{ "data": { "Foo": [ { "id": "2", "key": "foobar" } ] } }
Shouldn't both records got returned?
I'm not using relay and I'm using the example code (w/o relay) you provide the other day to build the filter set.

cls.aliased returns None periodically

Hi! I'm using this library with Fast API and experiencing an issue with filters with joins. Sometimes this piece of code returns None:

    @classmethod
    def _aliases_from_query(cls, query: Query) -> 'Dict[str, _MapperEntity]':
        """
        Get aliases from SQLAlchemy query.

        Args:
            query: SQLAlchemy query.

        Returns:
            Dictionary of model aliases.

        """
        aliases = {
            (mapper._target, mapper.name): mapper.entity
            for mapper in query._join_entities
        }

        return aliases

mapper.entity is None from time to time. This is because it uses weakref and entity object gets garbage collected or something like that I didn't dig that much. When I try to join the table once more sqlalchemy complains that it's already joined. Maybe you @art1415926535 could give some advice on how to fix that? Thanks

ambiguous column exception in _get_query in connection_field.py

the query generated by subquery when using filters unambiguously labeling columns resulting in an ambiguous column exception.

long story short, I have a polymorphic database, so id is shared between a base class and the filtered child class. that seems to be the root of the issue. after playing around for a while I found subquery.subquery() has a with_labels argument you can pass. setting that to True fixed the issue for me

e.g. connection_field.py line 250

      aliased_model = aliased(self.model, subquery.subquery(with_labels=True))

How to use variables with filters

Haven't managed to use variables with filters, is there a way to do so?

for instance I'd like to have sth like

query users($filter: UserFilter) { users(filters: $filter) { edges { node { id name } } } }

with variables:

{ "filter": { "name": "joe" } }

data_loader KeyError when using sqlalchemy 1.4.0b1

sqlalchemy 1.4.0b1 was released recently, in many ways attempting to serve as a potential migration point for a more dramatic series of API changes currently planned for release 2.0 of SQLAlchemy. The beta version can be installed by passing --pre flag when installing the library, such as:

py -m pip install --pre sqlalchemy

See issue #38 for my test case, with the following changes:

using sqlalchemy version 1.4.0b1 instead of version 1.3.20

My flask client

from flask import Flask
from flask_graphql import GraphQLView

app = Flask(__name__)
app.debug = True

app.add_url_rule(
    '/graphql',
    view_func=GraphQLView.as_view(
        'graphql',
        schema=schema,
        graphiql=True  # for having the GraphiQL interface
    )
)

@app.teardown_appcontext
def shutdown_session(exception=None):
    db_session.remove()

if __name__ == '__main__':
    # for this to work in jupyter notebook
    from werkzeug.serving import run_simple
    run_simple('localhost', 9000, app)

test query

query {
  allSongs {
    edges {
      node {
        name
        tags {
          edges {
            node {
              name
            }
          }
        }
      }
    }
  }
}

error in response

{
  "errors": [
    {
      "message": "items",
      "locations": [
        {
          "line": 6,
          "column": 9
        }
      ],
      "path": [
        "allSongs",
        "edges",
        0,
        "node",
        "tags"
      ]
    },
    {
      "message": "items",
      "locations": [
        {
          "line": 6,
          "column": 9
        }
      ],
      "path": [
        "allSongs",
        "edges",
        1,
        "node",
        "tags"
      ]
    },
    {
      "message": "items",
      "locations": [
        {
          "line": 6,
          "column": 9
        }
      ],
      "path": [
        "allSongs",
        "edges",
        2,
        "node",
        "tags"
      ]
    }
  ],
  "data": {
    "allSongs": {
      "edges": [
        {
          "node": {
            "name": "素晴らしい日々",
            "tags": null
          }
        },
        {
          "node": {
            "name": "水槽のフール",
            "tags": null
          }
        },
        {
          "node": {
            "name": "despacito",
            "tags": null
          }
        }
      ]
    }
  }
}

server side error

An error occurred while resolving field SongNode.tags
Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 324, in _get_or_create_data_loader
    current_data_loader: ModelLoader = data_loaders[data_loader_key]
KeyError: ('allSongs', 'edges', 'node', 'tags')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\sql\base.py", line 1104, in __getattr__
    return self._index[key]
KeyError: 'items'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executor.py", line 452, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executors\sync.py", line 16, in execute
    return fn(*args, **kwargs)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 356, in connection_resolver
    data_loader: ModelLoader = cls._get_or_create_data_loader(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 326, in _get_or_create_data_loader
    current_data_loader = ModelLoader(type(root), model, info, args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 137, in __init__
    self.parent_model_pks: 'Tuple[str, ...]' = self._get_model_pks(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 197, in _get_model_pks
    for name, c in inspection.inspect(model).columns.items()
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\sql\base.py", line 1106, in __getattr__
    util.raise_(AttributeError(key), replace_context=err)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\util\compat.py", line 180, in raise_
    raise exception
AttributeError: items
Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executor.py", line 452, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executors\sync.py", line 16, in execute
    return fn(*args, **kwargs)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 356, in connection_resolver
    data_loader: ModelLoader = cls._get_or_create_data_loader(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 326, in _get_or_create_data_loader
    current_data_loader = ModelLoader(type(root), model, info, args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 137, in __init__
    self.parent_model_pks: 'Tuple[str, ...]' = self._get_model_pks(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 197, in _get_model_pks
    for name, c in inspection.inspect(model).columns.items()
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\sql\base.py", line 1106, in __getattr__
    util.raise_(AttributeError(key), replace_context=err)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\util\compat.py", line 180, in raise_
    raise exception
graphql.error.located_error.GraphQLLocatedError: items

An error occurred while resolving field SongNode.tags
Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 324, in _get_or_create_data_loader
    current_data_loader: ModelLoader = data_loaders[data_loader_key]
KeyError: ('allSongs', 'edges', 'node', 'tags')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\sql\base.py", line 1104, in __getattr__
    return self._index[key]
KeyError: 'items'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executor.py", line 452, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executors\sync.py", line 16, in execute
    return fn(*args, **kwargs)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 356, in connection_resolver
    data_loader: ModelLoader = cls._get_or_create_data_loader(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 326, in _get_or_create_data_loader
    current_data_loader = ModelLoader(type(root), model, info, args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 137, in __init__
    self.parent_model_pks: 'Tuple[str, ...]' = self._get_model_pks(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 197, in _get_model_pks
    for name, c in inspection.inspect(model).columns.items()
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\sql\base.py", line 1106, in __getattr__
    util.raise_(AttributeError(key), replace_context=err)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\util\compat.py", line 180, in raise_
    raise exception
AttributeError: items
Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executor.py", line 452, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executors\sync.py", line 16, in execute
    return fn(*args, **kwargs)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 356, in connection_resolver
    data_loader: ModelLoader = cls._get_or_create_data_loader(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 326, in _get_or_create_data_loader
    current_data_loader = ModelLoader(type(root), model, info, args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 137, in __init__
    self.parent_model_pks: 'Tuple[str, ...]' = self._get_model_pks(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 197, in _get_model_pks
    for name, c in inspection.inspect(model).columns.items()
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\sql\base.py", line 1106, in __getattr__
    util.raise_(AttributeError(key), replace_context=err)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\util\compat.py", line 180, in raise_
    raise exception
graphql.error.located_error.GraphQLLocatedError: items

An error occurred while resolving field SongNode.tags
Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 324, in _get_or_create_data_loader
    current_data_loader: ModelLoader = data_loaders[data_loader_key]
KeyError: ('allSongs', 'edges', 'node', 'tags')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\sql\base.py", line 1104, in __getattr__
    return self._index[key]
KeyError: 'items'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executor.py", line 452, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executors\sync.py", line 16, in execute
    return fn(*args, **kwargs)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 356, in connection_resolver
    data_loader: ModelLoader = cls._get_or_create_data_loader(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 326, in _get_or_create_data_loader
    current_data_loader = ModelLoader(type(root), model, info, args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 137, in __init__
    self.parent_model_pks: 'Tuple[str, ...]' = self._get_model_pks(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 197, in _get_model_pks
    for name, c in inspection.inspect(model).columns.items()
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\sql\base.py", line 1106, in __getattr__
    util.raise_(AttributeError(key), replace_context=err)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\util\compat.py", line 180, in raise_
    raise exception
AttributeError: items
Traceback (most recent call last):
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executor.py", line 452, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphql\execution\executors\sync.py", line 16, in execute
    return fn(*args, **kwargs)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 356, in connection_resolver
    data_loader: ModelLoader = cls._get_or_create_data_loader(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 326, in _get_or_create_data_loader
    current_data_loader = ModelLoader(type(root), model, info, args)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 137, in __init__
    self.parent_model_pks: 'Tuple[str, ...]' = self._get_model_pks(
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\graphene_sqlalchemy_filter\connection_field.py", line 197, in _get_model_pks
    for name, c in inspection.inspect(model).columns.items()
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\sql\base.py", line 1106, in __getattr__
    util.raise_(AttributeError(key), replace_context=err)
  File "c:\users\geoffrey\appdata\local\programs\python\python38\lib\site-packages\sqlalchemy\util\compat.py", line 180, in raise_
    raise exception
graphql.error.located_error.GraphQLLocatedError: items

I feel like this may be related to #38 but I'm not sure, opening a separate issue just in case.

How to use async resolver for filter?

For example

class UserFilter(FilterSet):
    is_cool = graphene.Boolean()

    @classmethod
    async def is_cool_filter(cls, info, query, value):
        returned_list= await async_func()

        if value:
            filter_ = query.id.in_(returned_list)
        else:
            filter_ = query.id.in_(None)

        return query, filter_

graphene-sqlalchemy-filter not working in nested graphQL query.

I am having issues in implementing graphene-sqlalchemy-filter in nested graphQL query. It is working as expected with normal graphQL query but when implemented in nested query the filter seems to not work.

I have two models:

  • Group model
  • Task model
class GroupModel(Base):
    __tablename__ = "groups"
    id = Column(Integer, primary_key=True)
    name = Column(Text, index=True)
    tasks = relationship(
        "TaskModel",
        backref=backref("circle", lazy='bulk'),
        lazy='bulk',
        cascade="all, delete-orphan",
    )
class TaskModel(Base):
    __tablename__ = "tasks"
    id = Column(Integer, primary_key=True)
    group_id = Column(Integer, ForeignKey("groups.id"), nullable=False)
    title = Column(Text, nullable=False)
    done = Column(Boolean, nullable=False, index=True)
    categories = Column(
        Text,
        default="General",
    )

#The code for filtering :

class GroupTodoFilter(FilterSet):
    class Meta:
        model = TaskModel
        fields = {
            'title': ['eq'],
            'done': ['eq'],
            'categories': ['eq'],
        }


class GroupFilterableConnectionField(FilterableConnectionField):
    filters = {TaskModel: GroupTaskFilter()}


class TaskFilter(SQLAlchemyObjectType):
    class Meta:
        model = TaskModel
        interfaces = (relay.Node, )
        connection_field_factory = GroupFilterableConnectionField.factory


class TaskFilterConnection_1(Connection):
    class Meta:
        node = TaskFilter


class GroupFilterNode(SQLAlchemyObjectType):
    class Meta:
        model = GroupModel
        interfaces = (relay.Node, )
        connection_field_factory = GroupFilterableConnectionField.factory


class GroupFilterConnection(Connection):
    class Meta:
        node = GroupFilterNode

`

in schema:

all_task = GroupFilterableConnectionField(TaskFilterConnection_1)
  all_group_filters = GroupFilterableConnectionField(
     GroupFilterConnection)
filter_group = relay.Node.Field(GroupFilterNode)

Queries

query{
  allGroupFilters{
    edges{
      node{
        tasks(filters:{done:true}){
          edges{
            node{
              id
              groupId
              categories
              done
              title
            }
          }
        }
      }
    }
  }
}

query{
  allTask (filters:{done:false}){
    edges{
      node{
        id
        title
        groupId
        categories
        done
      }
    }
  }
}
query($id: ID!) {
  filterGroup (id: $id) {
    tasks (filters:
       {categories:"Health"}
    )
      {
      edges {
        node {
          id
          group {
            id
          }
          title
          done
          categories
        }
      }
    }
  }
}`

Task query is working alright but filterGroup and allFilterGroup query have no filtering affect.

TypeError: 'String' object is not callable

First off, this is awesome! I'm still not 100% familiar with the intricacies of SQLAlchemy and it would have taken me a while to write something as complete as this library. Thanks!


I have a relatively simple setup.
Model:

class Transaction(Base):
    # ...
    memo = Column(Text)
    # ...

Filter:

class TransactionFilter(FilterSet):
    class Meta:
        model = Transaction
        fields = {
            'memo': [...],
        }

However, when I run this, I get the following error:

Traceback (most recent call last):
  ...
    class TransactionFilter(FilterSet):
  File "/home/aaron/.virtualenvs/api/lib/python3.6/site-packages/graphene/utils/subclass_with_meta.py", line 52, in __init_subclass__
    super_class.__init_subclass_with_meta__(**options)
  File "/home/aaron/.virtualenvs/api/lib/python3.6/site-packages/graphene_sqlalchemy_filter/filters.py", line 262, in __init_subclass_with_meta__
    filters_fields = cls._generate_default_filters(model, fields)
  File "/home/aaron/.virtualenvs/api/lib/python3.6/site-packages/graphene_sqlalchemy_filter/filters.py", line 515, in _generate_default_filters
    expressions, field_name, field_type, field_object['nullable']
  File "/home/aaron/.virtualenvs/api/lib/python3.6/site-packages/graphene_sqlalchemy_filter/filters.py", line 563, in _generate_filter_fields
    filter_field = field_type(description=doc)
TypeError: 'String' object is not callable

The error is coming from _generate_default_filters from the following line:

field_type = convert_sqlalchemy_type(
    column_type, field_object['column']
)

The field_type that is returned is an instance of graphene.String(), which is itself not callable. I'm still familiarizing myself with the code, but I think this should be getting the class String and not an instance. My workaround (that seems to work) is to tack __class__ on the end of that call:

field_type = convert_sqlalchemy_type(
    column_type, field_object['column']
).__class__

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.