Skip to content

Creating complex "where" filters #231

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
maquino1985 opened this issue Jun 19, 2019 · 2 comments
Closed

Creating complex "where" filters #231

maquino1985 opened this issue Jun 19, 2019 · 2 comments

Comments

@maquino1985
Copy link

maquino1985 commented Jun 19, 2019

I was trying to create a way to implement filters that could create complex where clauses with groupings and I was beginning to think it was not possible to create a recursive filter when I accidentally made it work. So I just wanted to share this code in case anyone else finds it helpful:

I added a where filter to a subclassed UnsortedSQLAlchemyConnectionField

class SQLAlchemyFilteredConnectionField(UnsortedSQLAlchemyConnectionField):
    def __init__(self, type_, *args, **kwargs):
        model = type_._meta.model
        kwargs.setdefault("where", create_filter_argument(model))
        super(SQLAlchemyFilteredConnectionField, self).__init__(type_, *args, **kwargs)

the magic happens in create_filter_argument:

def create_filter_field(column):
    graphene_type = convert_sqlalchemy_type(column.type, column)
    if graphene_type.__class__ == Field:
        return None

    name = "{}Filter".format(str(graphene_type.__class__))
    if name in field_cache:
        return Field(field_cache[name])

    fields = OrderedDict((key, Field(graphene_type.__class__))
                         for key in ["equal", "notEqual", "lessThan", "greaterThan", "like"])
    fields['in'] = Field(List(graphene_type.__class__))
    field_class: InputObjectType = type(name, (FilterField, InputObjectType), {})
    field_class._meta.fields.update(fields)

    field_cache[name] = field_class
    return Field(field_class)

def create_filter_argument(cls):
    name = "{}Filter".format(cls.__name__)
    if name in argument_cache:
        return Argument(argument_cache[name])
    import re

    NAME_PATTERN = r"^[_a-zA-Z][_a-zA-Z0-9]*$"
    COMPILED_NAME_PATTERN = re.compile(NAME_PATTERN)

    fields = OrderedDict((column.name, field)
                         for column, field in [(column, create_filter_field(column))
                                               for column in inspect(cls).columns.values()] if field and COMPILED_NAME_PATTERN.match(column.name))
    argument_class: InputObjectType = type(name, (FilterArgument, InputObjectType), {})
    argument_class._meta.fields.update(fields)

    nested_argument_class: InputObjectType = copy.deepcopy(argument_class) # not sure if necessary
    argument_class._meta.fields['or'] = Argument(nested_argument_class)
    argument_class._meta.fields['and'] = Argument(nested_argument_class)
    argument_cache[name] = argument_class

    return Argument(argument_class)

This might not be the best or the prettiest way to achieve this, but it allows you to then create filters that refer to themselves recursively so now you can make a graphene request that looks like this

{
  allRequests(where: {visibleId: {equal: 1}, or: {or: {or: {}}, and: {or: {and: {or: {}}}}}}) {
    edges {
      node {
        id
        label
      }
    }
  }
}
@erikwrede
Copy link
Member

I am closing all old issues&PRs related to filtering. The discussion will continue in #347 (WIP). A proposal for the implementation of filters is currently being worked on and will be posted there once it is ready.

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related topics referencing this issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants