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

add Django 5 support #206

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ ignore = E203, E266, E501, W503
max-line-length = 80
max-complexity = 18
select = B,C,E,F,W,T4,B9
exclude =
.venv
.tox
dist
21 changes: 18 additions & 3 deletions .github/workflows/cicd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ jobs:
strategy:
matrix:
include:
- python: 3.7
django: 3.2
toxenv: py37-django32
- python: 3.8
django: 3.2
toxenv: py38-django32
Expand All @@ -37,6 +34,24 @@ jobs:
- python: '3.10'
django: 4.1
toxenv: py310-django41
- python: '3.10'
django: 5.0
toxenv: py310-django50
- python: '3.11'
django: 5.0
toxenv: py311-django50
- python: '3.12'
django: 5.0
toxenv: py312-django50
- python: '3.10'
django: 5.1
toxenv: py310-django51
- python: '3.11'
django: 5.1
toxenv: py311-django51
- python: '3.12'
django: 5.1
toxenv: py312-django51
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v1
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ __pycache__
.coverage*
.tox/
.pytest*/

.DS_Store

.venv/
12 changes: 6 additions & 6 deletions graphene_django_extras/base_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ def serialize(time):
if isinstance(time, datetime.datetime):
time = time.time()

assert isinstance(time, datetime.time), 'Received not compatible time "{}"'.format(
repr(time)
)
assert isinstance(
time, datetime.time
), 'Received not compatible time "{}"'.format(repr(time))
return time.isoformat()


Expand All @@ -168,9 +168,9 @@ def serialize(date):

if isinstance(date, datetime.datetime):
date = date.date()
assert isinstance(date, datetime.date), 'Received not compatible date "{}"'.format(
repr(date)
)
assert isinstance(
date, datetime.date
), 'Received not compatible date "{}"'.format(repr(date))
return date.isoformat()


Expand Down
86 changes: 66 additions & 20 deletions graphene_django_extras/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,26 @@
from functools import singledispatch

from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRel, GenericRelation
from django.contrib.contenttypes.fields import (
GenericForeignKey,
GenericRel,
GenericRelation,
)
from django.db import models
from django.utils.encoding import force_str
from graphene import ID, UUID, Boolean, Dynamic, Enum, Field, Float, Int, List, NonNull, String
from graphene import (
ID,
UUID,
Boolean,
Dynamic,
Enum,
Field,
Float,
Int,
List,
NonNull,
String,
)
from graphene.types.json import JSONString
from graphene.utils.str_converters import to_camel_case
from graphene_django.compat import ArrayField, HStoreField, JSONField, RangeField
Expand All @@ -30,9 +46,9 @@

def assert_valid_name(name):
"""Helper to assert that provided names are valid."""
assert COMPILED_NAME_PATTERN.match(name), 'Names must match /{}/ but "{}" does not.'.format(
NAME_PATTERN, name
)
assert COMPILED_NAME_PATTERN.match(
name
), 'Names must match /{}/ but "{}" does not.'.format(NAME_PATTERN, name)


def convert_choice_name(name):
Expand All @@ -59,7 +75,9 @@ def get_choices(choices):
yield name, value, description


def convert_django_field_with_choices(field, registry=None, input_flag=None, nested_field=False):
def convert_django_field_with_choices(
field, registry=None, input_flag=None, nested_field=False
):
choices = getattr(field, "choices", None)
if choices:
meta = field.model._meta
Expand Down Expand Up @@ -109,14 +127,18 @@ def construct_fields(

if settings.DEBUG:
if input_flag == "create":
_model_fields = sorted(_model_fields, key=lambda f: (not is_required(f[1]), f[0]))
_model_fields = sorted(
_model_fields, key=lambda f: (not is_required(f[1]), f[0])
)
elif not input_flag:
_model_fields = sorted(_model_fields, key=lambda f: f[0])

fields = OrderedDict()

if input_flag == "delete":
converted = convert_django_field_with_choices(dict(_model_fields)["id"], registry)
converted = convert_django_field_with_choices(
dict(_model_fields)["id"], registry
)
fields["id"] = converted
else:
for name, field in _model_fields:
Expand All @@ -126,7 +148,9 @@ def construct_fields(
nested_field = name in nested_fields
is_not_in_only = only_fields and name not in only_fields
# is_already_created = name in options.fields
is_excluded = exclude_fields and name in exclude_fields # or is_already_created
is_excluded = (
exclude_fields and name in exclude_fields
) # or is_already_created
# https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ForeignKey.related_query_name
is_no_backref = str(name).endswith("+")
# if is_not_in_only or is_excluded or is_no_backref:
Expand All @@ -144,15 +168,19 @@ def construct_fields(
):
continue

converted = convert_django_field_with_choices(field, registry, input_flag, nested_field)
converted = convert_django_field_with_choices(
field, registry, input_flag, nested_field
)
fields[name] = converted
return fields


@singledispatch
def convert_django_field(field, registry=None, input_flag=None, nested_field=False):
raise Exception(
"Don't know how to convert the Django field {} ({})".format(field, field.__class__)
"Don't know how to convert the Django field {} ({})".format(
field, field.__class__
)
)


Expand Down Expand Up @@ -212,7 +240,9 @@ def convert_field_to_boolean(field, registry=None, input_flag=None, nested_field


@convert_django_field.register(models.NullBooleanField)
def convert_field_to_nullboolean(field, registry=None, input_flag=None, nested_field=False):
def convert_field_to_nullboolean(
field, registry=None, input_flag=None, nested_field=False
):
return Boolean(
description=field.help_text or field.verbose_name,
required=is_required(field) and input_flag == "create",
Expand Down Expand Up @@ -246,7 +276,9 @@ def convert_date_to_string(field, registry=None, input_flag=None, nested_field=F


@convert_django_field.register(models.DateTimeField)
def convert_datetime_to_string(field, registry=None, input_flag=None, nested_field=False):
def convert_datetime_to_string(
field, registry=None, input_flag=None, nested_field=False
):
return CustomDateTime(
description=field.help_text or field.verbose_name,
required=is_required(field) and input_flag == "create",
Expand Down Expand Up @@ -279,7 +311,9 @@ def dynamic_type():


@convert_django_field.register(models.ManyToManyField)
def convert_field_to_list_or_connection(field, registry=None, input_flag=None, nested_field=False):
def convert_field_to_list_or_connection(
field, registry=None, input_flag=None, nested_field=False
):
model = get_related_model(field)

def dynamic_type():
Expand Down Expand Up @@ -315,7 +349,9 @@ def dynamic_type():
@convert_django_field.register(GenericRel)
@convert_django_field.register(models.ManyToManyRel)
@convert_django_field.register(models.ManyToOneRel)
def convert_many_rel_to_djangomodel(field, registry=None, input_flag=None, nested_field=False):
def convert_many_rel_to_djangomodel(
field, registry=None, input_flag=None, nested_field=False
):
model = field.related_model

def dynamic_type():
Expand Down Expand Up @@ -343,12 +379,16 @@ def dynamic_type():

@convert_django_field.register(models.OneToOneField)
@convert_django_field.register(models.ForeignKey)
def convert_field_to_djangomodel(field, registry=None, input_flag=None, nested_field=False):
def convert_field_to_djangomodel(
field, registry=None, input_flag=None, nested_field=False
):
model = get_related_model(field)

def dynamic_type():
# Avoid create field for auto generate OneToOneField product of an inheritance
if isinstance(field, models.OneToOneField) and issubclass(field.model, field.related_model):
if isinstance(field, models.OneToOneField) and issubclass(
field.model, field.related_model
):
return
if input_flag and not nested_field:
return ID(
Expand Down Expand Up @@ -433,7 +473,9 @@ def dynamic_type():


@convert_django_field.register(ArrayField)
def convert_postgres_array_to_list(field, registry=None, input_flag=None, nested_field=False):
def convert_postgres_array_to_list(
field, registry=None, input_flag=None, nested_field=False
):
base_type = convert_django_field(field.base_field)
if not isinstance(base_type, (List, NonNull)):
base_type = type(base_type)
Expand All @@ -446,15 +488,19 @@ def convert_postgres_array_to_list(field, registry=None, input_flag=None, nested

@convert_django_field.register(HStoreField)
@convert_django_field.register(JSONField)
def convert_postgres_field_to_string(field, registry=None, input_flag=None, nested_field=False):
def convert_postgres_field_to_string(
field, registry=None, input_flag=None, nested_field=False
):
return JSONString(
description=field.help_text or field.verbose_name,
required=is_required(field) and input_flag == "create",
)


@convert_django_field.register(RangeField)
def convert_postgres_range_to_string(field, registry=None, input_flag=None, nested_field=False):
def convert_postgres_range_to_string(
field, registry=None, input_flag=None, nested_field=False
):
inner_type = convert_django_field(field.base_field)
if not isinstance(inner_type, (List, NonNull)):
inner_type = type(inner_type)
Expand Down
12 changes: 9 additions & 3 deletions graphene_django_extras/directives/date.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ def _format_relativedelta(rdelta, full=False, two_days=False, original_dt=None):
def _format_time_ago(dt, now=None, full=False, ago_in=False, two_days=False):
if not isinstance(dt, timedelta):
if now is None:
now = timezone.localtime(timezone=timezone.get_fixed_timezone(-int(t.timezone / 60)))
now = timezone.localtime(
timezone=timezone.get_fixed_timezone(-int(t.timezone / 60))
)

original_dt = dt
dt = _parse(dt)
Expand Down Expand Up @@ -201,7 +203,9 @@ def _format_dt(dt, format="default"):
else:
if temp_format != "":
if temp_format in FORMATS_MAP:
translate_format_list.append(FORMATS_MAP.get(temp_format, ""))
translate_format_list.append(
FORMATS_MAP.get(temp_format, "")
)
else:
return None
if str_in_dict_keys(char, FORMATS_MAP):
Expand Down Expand Up @@ -236,7 +240,9 @@ def get_args():

@staticmethod
def resolve(value, directive, root, info, **kwargs):
format_argument = [arg for arg in directive.arguments if arg.name.value == "format"]
format_argument = [
arg for arg in directive.arguments if arg.name.value == "format"
]
format_argument = format_argument[0] if len(format_argument) > 0 else None

custom_format = format_argument.value.value if format_argument else "default"
Expand Down
6 changes: 5 additions & 1 deletion graphene_django_extras/directives/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ def resolve(value, directive, root, info, **kwargs):
class SampleGraphQLDirective(BaseExtraGraphQLDirective):
@staticmethod
def get_args():
return {"k": GraphQLArgument(GraphQLNonNull(GraphQLInt), description="Value to default to")}
return {
"k": GraphQLArgument(
GraphQLNonNull(GraphQLInt), description="Value to default to"
)
}

@staticmethod
def resolve(value, directive, root, info, **kwargs):
Expand Down
Loading