Skip to content
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

Reduce cognitive complexity and typo fixes #1457

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ad447d9
Remove unnecessary if statement
lilac-supernova-2 Aug 26, 2023
0e1b605
Keep 'if' above same as before, fix lint issue
lilac-supernova-2 Aug 26, 2023
cd6e042
Fix Star Wars spaceship name
lilac-supernova-2 Aug 26, 2023
ea77bc7
Fix some typos in comments
lilac-supernova-2 Aug 26, 2023
6580228
Rename filename with typo
lilac-supernova-2 Aug 26, 2023
1b00e67
Update import
lilac-supernova-2 Aug 26, 2023
5c52096
Snake case fixes
lilac-supernova-2 Aug 26, 2023
2a888ac
Add possibly missing import of DjangoFilterConnectionField
lilac-supernova-2 Aug 26, 2023
b639034
Actually, let's not do this to avoid circular imports
lilac-supernova-2 Aug 26, 2023
d873220
Remove nesting for readability
lilac-supernova-2 Aug 26, 2023
67373c7
Rename function
lilac-supernova-2 Aug 26, 2023
722daf1
Typo fixes
lilac-supernova-2 Aug 26, 2023
34e61c6
More typo fixes
lilac-supernova-2 Aug 26, 2023
988b308
Refactor get_filtering_args_from_filterset into more functions to red…
lilac-supernova-2 Aug 26, 2023
cb92d6e
Add new tests-repeat command to Makefile to be able to run unit tests…
lilac-supernova-2 Aug 26, 2023
19d5558
Add 'self' argument
lilac-supernova-2 Aug 26, 2023
34db851
Snake case formatting fix
lilac-supernova-2 Aug 26, 2023
98fd498
Refactor validation function to reduce complexity
lilac-supernova-2 Aug 26, 2023
a94a89d
Linting
lilac-supernova-2 Aug 26, 2023
59c0117
Refactor execute_graphql_request function to reduce complexity
lilac-supernova-2 Aug 26, 2023
e263840
Linting
lilac-supernova-2 Aug 26, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ dev-setup:
tests:
PYTHONPATH=. pytest graphene_django --cov=graphene_django -vv

.PHONY: tests-repeat ## Run unit tests 100 times to possibly identify flaky unit tests (and run them in parallel)
tests-repeat:
PYTHONPATH=. pytest graphene_django --cov=graphene_django -vv --count 100 -n logical

.PHONY: format ## Format code
format:
black graphene_django examples setup.py
Expand Down
2 changes: 1 addition & 1 deletion docs/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Graphene-Django can be customised using settings. This page explains each settin
Usage
-----

Add settings to your Django project by creating a Dictonary with name ``GRAPHENE`` in the project's ``settings.py``:
Add settings to your Django project by creating a Dictionary with name ``GRAPHENE`` in the project's ``settings.py``:

.. code:: python

Expand Down
2 changes: 1 addition & 1 deletion examples/cookbook/dummy_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@
"fields": {
"category": 3,
"name": "Newt",
"notes": "Braised and Confuesd"
"notes": "Braised and Confused"
},
"model": "ingredients.ingredient",
"pk": 5
Expand Down
14 changes: 7 additions & 7 deletions examples/starwars/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ def initialize():

# Yeah, technically it's Corellian. But it flew in the service of the rebels,
# so for the purposes of this demo it's a rebel ship.
falcon = Ship(id="4", name="Millenium Falcon", faction=rebels)
falcon = Ship(id="4", name="Millennium Falcon", faction=rebels)
falcon.save()

homeOne = Ship(id="5", name="Home One", faction=rebels)
homeOne.save()
home_one = Ship(id="5", name="Home One", faction=rebels)
home_one.save()

tieFighter = Ship(id="6", name="TIE Fighter", faction=empire)
tieFighter.save()
tie_fighter = Ship(id="6", name="TIE Fighter", faction=empire)
tie_fighter.save()

tieInterceptor = Ship(id="7", name="TIE Interceptor", faction=empire)
tieInterceptor.save()
tie_interceptor = Ship(id="7", name="TIE Interceptor", faction=empire)
tie_interceptor.save()

executor = Ship(id="8", name="Executor", faction=empire)
executor.save()
Expand Down
2 changes: 1 addition & 1 deletion examples/starwars/tests/test_mutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def test_mutations():
{"node": {"id": "U2hpcDox", "name": "X-Wing"}},
{"node": {"id": "U2hpcDoy", "name": "Y-Wing"}},
{"node": {"id": "U2hpcDoz", "name": "A-Wing"}},
{"node": {"id": "U2hpcDo0", "name": "Millenium Falcon"}},
{"node": {"id": "U2hpcDo0", "name": "Millennium Falcon"}},
{"node": {"id": "U2hpcDo1", "name": "Home One"}},
{"node": {"id": "U2hpcDo5", "name": "Peter"}},
]
Expand Down
2 changes: 1 addition & 1 deletion graphene_django/debug/middleware.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.db import connections

from .exception.formating import wrap_exception
from .exception.formatting import wrap_exception
from .sql.tracking import unwrap_cursor, wrap_cursor
from .types import DjangoDebug

Expand Down
2 changes: 1 addition & 1 deletion graphene_django/filter/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class Meta:
"name": ["exact", "contains"],
}

# Those are actually usable with our Query fixture bellow
# Those are actually usable with our Query fixture below
tags__contains = ArrayFilter(field_name="tags", lookup_expr="contains")
tags__overlap = ArrayFilter(field_name="tags", lookup_expr="overlap")
tags = ArrayFilter(field_name="tags", lookup_expr="exact")
Expand Down
4 changes: 2 additions & 2 deletions graphene_django/filter/tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,7 @@ class Query(ObjectType):

query = """
query NodeFilteringQuery {
allReporters(orderBy: "-firtsnaMe") {
allReporters(orderBy: "-firstname") {
edges {
node {
firstName
Expand All @@ -802,7 +802,7 @@ class Query(ObjectType):
assert result.errors


def test_order_by_is_perserved():
def test_order_by_is_preserved():
class ReporterType(DjangoObjectType):
class Meta:
model = Reporter
Expand Down
160 changes: 107 additions & 53 deletions graphene_django/filter/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,109 @@
from .filterset import custom_filterset_factory, setup_filterset


def get_field_type(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)
if object_type:
object_type_field = object_type._meta.fields.get(field_name)
if object_type_field:
field_type = object_type_field.type
if isinstance(field_type, graphene.NonNull):
field_type = field_type.of_type
return field_type
return None
if not object_type:
return None

object_type_field = object_type._meta.fields.get(field_name)
if not object_type_field:
return None

field_type = object_type_field.type
if isinstance(field_type, graphene.NonNull):
field_type = field_type.of_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):
"""
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.
"""
from ..forms.converter import convert_form_field

args = {}
model = filterset_class._meta.model
Expand All @@ -43,55 +125,27 @@ def get_filtering_args_from_filterset(filterset_class, type):
isinstance(filter_field, TypedFilter)
and filter_field.input_type is not None
):
# First check if the filter input type has been explicitely given
# First check if the filter input type has been explicitly given
field_type = filter_field.input_type
else:
if name not in filterset_class.declared_filters or isinstance(
filter_field, TypedFilter
):
# Get the filter field for filters that are no explicitly declared.
if filter_type == "isnull":
field_type = graphene.Boolean
else:
model_field = get_model_field(model, filter_field.field_name)

# 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
)
(
field_type,
form_field,
) = get_field_type_and_form_field_for_implicit_filter(
model, filter_type, filter_field, registry, required
)

if not field_type:
# 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
form_field = form_field or filter_field.field
field_type = convert_form_field(form_field).get_type()

if isinstance(filter_field, ListFilter) or isinstance(
filter_field, RangeFilter
):
# 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 = get_field_type_for_explicit_filter(
filter_field, form_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.
if is_filter_list_or_range(filter_field):
field_type = graphene.List(field_type)

args[name] = graphene.Argument(
Expand Down
4 changes: 2 additions & 2 deletions graphene_django/forms/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from graphene.utils.str_converters import to_camel_case

from ..converter import BlankValueField
from ..types import ErrorType # noqa Import ErrorType for backwards compatability
from ..types import ErrorType # noqa Import ErrorType for backwards compatibility
from .mutation import fields_for_form


Expand Down Expand Up @@ -60,7 +60,7 @@ def mutate(_root, _info, data):
and isinstance(object_type._meta.fields[name], BlankValueField)
):
# Field type BlankValueField here means that field
# with choises have been converted to enum
# with choices have been converted to enum
# (BlankValueField is using only for that task ?)
setattr(cls, name, cls.get_enum_cnv_cls_instance(name, object_type))
elif (
Expand Down
2 changes: 1 addition & 1 deletion graphene_django/tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class Meta:

class APNewsReporter(Reporter):
"""
This class only inherits from Reporter for testing multi table inheritence
This class only inherits from Reporter for testing multi table inheritance
similar to what you'd see in django-polymorphic
"""

Expand Down
2 changes: 1 addition & 1 deletion graphene_django/tests/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class ArticleConnection(Connection):

test = String()

def resolve_test():
def resolve_test(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The convention for the resolve_* methods for an ObjectType (which Connection is) is to use parent as the first argument, not self, since these are "implicit static methods": https://docs.graphene-python.org/en/latest/types/objecttypes/#naming-convention

return "test"

class Meta:
Expand Down
Loading