Closed
Description
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
}
}
}
}
Metadata
Metadata
Assignees
Labels
No labels