Skip to content
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: 2 additions & 2 deletions graphene_django_pretty/auth/decorators.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from typing import List, Union
from functools import partial, wraps
from typing import List, Union

from django.contrib.auth import get_user_model
from graphql.execution.execute import GraphQLResolveInfo
from graphene_django_pretty.auth.exceptions import PermissionDeniedError

from graphene_django_pretty.auth.exceptions import PermissionDeniedError

User = get_user_model()

Expand Down
2 changes: 1 addition & 1 deletion graphene_django_pretty/mutations/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import graphene
from graphene.utils import get_unbound_function, props
from fs_graphene_django.mutations.mutations.utils import decorate_mutate_func
from graphene.utils import get_unbound_function, props


class BaseMutation(graphene.Mutation):
Expand Down
110 changes: 85 additions & 25 deletions graphene_django_pretty/types/base.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
import warnings
import graphene

from copy import deepcopy
from typing import Any, Dict, List, Type

from fs_graphene_django.types.registry import FieldDescriptionDrivenRegistry
from graphene import Field
import graphene
from graphene.relay import Connection, Node
from graphene.types.interface import Interface
from graphene.types.objecttype import ObjectType, ObjectTypeOptions
from graphene.types.utils import yank_fields_from_attrs
from graphene_django import DjangoObjectType
from graphene_django.registry import Registry
from graphene_django.types import (
ALL_FIELDS,
DJANGO_FILTER_INSTALLED,
DjangoObjectTypeOptions,
construct_fields,
get_global_registry,
validate_fields,
)

from graphene_django_pretty.types.registry import FieldDescriptionDrivenRegistry
from graphene_django_pretty.types.utils import is_valid_django_model


Expand All @@ -36,7 +34,42 @@ class PatchedDjangoObjectTypeOptions(ObjectTypeOptions):
default_resolver = None


class BaseDjangoObjectType(ObjectType):
class BaseDjangoModelUnion(graphene.Union):
"""Base union type for django object types."""

class Meta:
abstract = True

@classmethod
def assert_type_subclass(cls, type_):
"""Assert if type is not DjangoObjectType subclass."""
assert issubclass(
type_,
DjangoObjectType,
), f'Must provide Django object types for Union {cls.__name__}.'

@classmethod
def __init_subclass_with_meta__(cls, types=None, **options):
if types:
for type_ in types:
cls.assert_type_subclass(type_)

super(BaseDjangoModelUnion, cls).__init_subclass_with_meta__(
types=types,
**options,
)

@classmethod
def resolve_type(cls, instance, info):
"""Resolving grapql type by models instance."""
if getattr(cls._meta, 'types', False):
for type_ in cls._meta.types:
if isinstance(instance, type_._meta.model):
return type_
return None


class BaseDjangoObjectType(DjangoObjectType):
"""
Redefined class for adding possibility of getting field description and auto add it to schema.
Class in general repeats DjangoObjectType, but adds defining of interface fields
Expand All @@ -45,7 +78,10 @@ class BaseDjangoObjectType(ObjectType):

id = graphene.ID(required=True, description='ID of the object')

@classmethod
class Meta:
abstract = True

@ classmethod
def __init_subclass_with_meta__(
cls,
model=None,
Expand Down Expand Up @@ -77,16 +113,20 @@ def __init_subclass_with_meta__(
'Registry, received "{}".'
).format(cls.__name__, registry)

assert filter_fields and filterset_class, "Can't set both filter_fields and filterset_class"
if filter_fields and filterset_class:
raise Exception("Can't set both filter_fields and filterset_class")

assert DJANGO_FILTER_INSTALLED and (filter_fields or filterset_class), (
"Can only set filter_fields or filterset_class if "
"Django-Filter is installed"
)
if not DJANGO_FILTER_INSTALLED and (filter_fields or filterset_class):
raise Exception(
(
"Can only set filter_fields or filterset_class if "
"Django-Filter is installed"
),
)

assert not (fields and exclude), (
"Cannot set both 'fields' and 'exclude' options on "
"DjangoObjectType {class_name}.".format(class_name=cls.__name__)
'DjangoObjectType {class_name}.'.format(class_name=cls.__name__)
)

if fields and fields != ALL_FIELDS and not isinstance(fields, (list, tuple)):
Expand All @@ -112,8 +152,14 @@ def __init_subclass_with_meta__(
)

django_fields = yank_fields_from_attrs(
construct_fields(model, registry, fields, exclude, convert_choices_to_enum),
_as=Field,
construct_fields(
model,
registry,
fields,
exclude,
convert_choices_to_enum,
),
_as=graphene.Field,
)

if use_connection is None and interfaces:
Expand All @@ -127,13 +173,13 @@ def __init_subclass_with_meta__(
connection_class = Connection

connection = connection_class.create_type(
"{}Connection".format(options.get("name") or cls.__name__),
'{}Connection'.format(options.get("name") or cls.__name__),
node=cls,
)

if connection is not None:
assert issubclass(connection, Connection), (
"The connection must be a Connection. Received {}"
'The connection must be a Connection. Received {}'
).format(connection.__name__)

if not _meta:
Expand All @@ -142,7 +188,10 @@ def __init_subclass_with_meta__(
# Added in this class instead original DjangoObjectType:
# Getting fields for django-model anf graphene model and merging them
interface_fields = cls.get_interface_fields(interfaces)
merged_fields = cls.merge_model_and_interface_fields(django_fields, interface_fields)
merged_fields = cls.merge_model_and_interface_fields(
django_fields,
interface_fields,
)

_meta.model = model
_meta.registry = registry
Expand Down Expand Up @@ -175,12 +224,21 @@ def get_interface_fields(cls, interfaces: List[Type[Interface]]) -> Dict[str, An
fields.update(interface._meta.fields)

for base in reversed(cls.__mro__):
fields.update(yank_fields_from_attrs(base.__dict__, _as=Field))
fields.update(
yank_fields_from_attrs(
base.__dict__,
_as=graphene.Field,
),
)

return fields

@classmethod
def merge_model_and_interface_fields(cls, django_fields, interface_fields) -> Dict[str, Any]:
def merge_model_and_interface_fields(
cls,
django_fields,
interface_fields,
) -> Dict[str, Any]:
"""
Returns merged fields with solved conflicts in field description.
For example, when Interface filed has got own field description and Django Models's Field has got own.
Expand All @@ -191,7 +249,9 @@ def merge_model_and_interface_fields(cls, django_fields, interface_fields) -> Di
for field_name, field_description in interface_fields.items():
# replace description from interface if it is not none.
if field_description.description:
merged_fields.get(field_name).description = field_description.description
merged_fields.get(
Copy link
Contributor

Choose a reason for hiding this comment

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

можешь плз поправить везде переносы

field_name,
).description = field_description.description

return merged_fields

Expand All @@ -201,7 +261,9 @@ def is_type_of(cls, root, info):
if isinstance(root, cls):
return True
if not is_valid_django_model(root.__class__):
raise Exception(('Received incompatible instance "{}".').format(root))
raise Exception(
('Received incompatible instance "{}".').format(root),
)

if cls._meta.model._meta.proxy:
model = root._meta.model
Expand All @@ -218,8 +280,6 @@ def node_resolver(cls, _, info, id): # noqa: WPS125
@classmethod
def NodeField(cls): # noqa: N802
"""New Node field."""
node_field = graphene.relay.Node.Field(cls)
node_field = Node.Field(cls)
node_field.wrap_resolve = lambda parent: cls.node_resolver
return node_field


4 changes: 4 additions & 0 deletions graphene_django_pretty/types/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ class FieldDescriptionDrivenRegistry(Registry):
def set_graphene_field_description(field, converted):
"""Updates kwargs for description key from model field definition of graphql description is empty."""
field_description = None
#TODO: Fix this converted field value
if not getattr(converted, 'kwargs', None):
return

graphene_field_description = converted.kwargs.get('description', None)
if not graphene_field_description:
field_description = field.verbose_name
Expand Down
16 changes: 15 additions & 1 deletion graphene_django_pretty/types/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Any
import inspect
from itertools import chain
from typing import Any

from django.db import models

Expand All @@ -15,3 +16,16 @@ def is_valid_django_model(model: Any) -> bool:
return issubclass(model, PolymorphicModel)

return is_django_model


def merge_querysets(*querysets, **kwargs):
"""Merge querysets function."""
sort_key = kwargs['sort_key']
reverse_needed = kwargs['reverse']
queryset = list(chain(*querysets))
if sort_key:
reverse = reverse_needed if reverse_needed else False
queryset.sort(key=lambda qs_item: getattr(
qs_item, sort_key), reverse=reverse)

return queryset
2 changes: 0 additions & 2 deletions graphene_django_pretty/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from functools import wraps

from server.core.auth.jwt.exceptions import PermissionDenied

try:
import django_filters # noqa

Expand Down
2 changes: 1 addition & 1 deletion graphene_django_pretty/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from graphene_django.views import GraphQLView
from fs_graphene_django.error.format import format_error
from graphene_django.views import GraphQLView


class PrettyGraphQLView(GraphQLView):
Expand Down
Loading