Refactor get_filtering_args_from_filterset into more functions to reduce cognitive complexity

This commit is contained in:
lilac-supernova-2 2023-08-26 02:42:06 -04:00 committed by GitHub
parent 34e61c6fce
commit 988b30845d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -10,7 +10,7 @@ from .filterset import custom_filterset_factory, setup_filterset
def get_field_type_from_registry(registry, model, field_name): def get_field_type_from_registry(registry, model, field_name):
""" """
Try to get a model field corresponding Graphql type from the DjangoObjectType. Try to get a model field corresponding GraphQL type from the DjangoObjectType.
""" """
object_type = registry.get_type_for_model(model) object_type = registry.get_type_for_model(model)
if not object_type: if not object_type:
@ -26,12 +26,91 @@ def get_field_type_from_registry(registry, model, field_name):
return field_type return field_type
def get_field_type_from_model_field(model_field, form_field, registry):
"""
Get the field type from the model field.
If the model field is a foreign key, then we need to get the type from the related model.
"""
if (
isinstance(form_field, forms.ModelChoiceField)
or isinstance(form_field, forms.ModelMultipleChoiceField)
or isinstance(form_field, GlobalIDMultipleChoiceField)
or isinstance(form_field, GlobalIDFormField)
):
# Foreign key have dynamic types and filtering on a foreign key actually means filtering on its ID.
return get_field_type_from_registry(registry, model_field.related_model, "id")
return get_field_type_from_registry(registry, model_field.model, model_field.name)
def get_form_field(model_field, filter_field, required):
"""
Retrieve the form field to use for the filter.
Get the form field either from:
# 1. the formfield corresponding to the model field
# 2. the field defined on filter
Returns None if no form field can be found.
"""
form_field = None
if hasattr(model_field, "formfield"):
form_field = model_field.formfield(required=required)
if not form_field:
form_field = filter_field.field
return form_field
def get_field_type_and_form_field_for_implicit_filter(
model, filter_type, filter_field, registry, required
):
"""
Get the filter type for filters that are not explicitly declared.
Returns a tuple of (field_type, form_field) where:
- field_type is the type of the filter argument
- form_field is the form field to use to validate the input value
"""
if filter_type == "isnull":
# Filter type is boolean, no form field.
return (graphene.Boolean, None)
model_field = get_model_field(model, filter_field.field_name)
form_field = get_form_field(model_field, filter_field, required)
# First try to get the matching field type from the GraphQL DjangoObjectType
if model_field:
field_type = get_field_type_from_model_field(model_field, form_field, registry)
return (field_type, form_field)
return (None, None)
def get_field_type_for_explicit_filter(filter_field, form_field):
"""
Fallback on converting the form field either because:
- it's an explicitly declared filters
- we did not manage to get the type from the model type
"""
from ..forms.converter import convert_form_field
form_field = form_field or filter_field.field
return convert_form_field(form_field).get_type()
def is_filter_list_or_range(filter_field):
"""
Determine if the filter is a ListFilter or RangeFilter.
"""
return isinstance(filter_field, ListFilter) or isinstance(filter_field, RangeFilter)
def get_filtering_args_from_filterset(filterset_class, type): def get_filtering_args_from_filterset(filterset_class, type):
""" """
Inspect a FilterSet and produce the arguments to pass to a Graphene Field. Inspect a FilterSet and produce the arguments to pass to a Graphene Field.
These arguments will be available to filter against in the GraphQL API. These arguments will be available to filter against in the GraphQL API.
""" """
from ..forms.converter import convert_form_field
args = {} args = {}
model = filterset_class._meta.model model = filterset_class._meta.model
@ -52,49 +131,21 @@ def get_filtering_args_from_filterset(filterset_class, type):
if name not in filterset_class.declared_filters or isinstance( if name not in filterset_class.declared_filters or isinstance(
filter_field, TypedFilter filter_field, TypedFilter
): ):
# Get the filter field for filters that are no explicitly declared. (
if filter_type == "isnull": field_type,
field_type = graphene.Boolean form_field,
else: ) = get_field_type_and_form_field_for_implicit_filter(
model_field = get_model_field(model, filter_field.field_name) model, filter_type, filter_field, registry, required
)
# Get the form field either from:
# 1. the formfield corresponding to the model field
# 2. the field defined on filter
if hasattr(model_field, "formfield"):
form_field = model_field.formfield(required=required)
if not form_field:
form_field = filter_field.field
# First try to get the matching field type from the GraphQL DjangoObjectType
if model_field:
if (
isinstance(form_field, forms.ModelChoiceField)
or isinstance(form_field, forms.ModelMultipleChoiceField)
or isinstance(form_field, GlobalIDMultipleChoiceField)
or isinstance(form_field, GlobalIDFormField)
):
# Foreign key have dynamic types and filtering on a foreign key actually means filtering on its ID.
field_type = get_field_type(
registry, model_field.related_model, "id"
)
else:
field_type = get_field_type(
registry, model_field.model, model_field.name
)
if not field_type: if not field_type:
# Fallback on converting the form field either because: field_type = get_field_type_for_explicit_filter(
# - it's an explicitly declared filters filter_field, form_field
# - we did not manage to get the type from the model type )
form_field = form_field or filter_field.field
field_type = convert_form_field(form_field).get_type()
if isinstance(filter_field, ListFilter) or isinstance( # Replace InFilter/RangeFilter filters (`in`, `range`) argument type to be a list of
filter_field, RangeFilter # the same type as the field. See comments in `replace_csv_filters` method for more details.
): if is_filter_list_or_range(filter_field):
# Replace InFilter/RangeFilter filters (`in`, `range`) argument type to be a list of
# the same type as the field. See comments in `replace_csv_filters` method for more details.
field_type = graphene.List(field_type) field_type = graphene.List(field_type)
args[name] = graphene.Argument( args[name] = graphene.Argument(