mirror of
				https://github.com/graphql-python/graphene-django.git
				synced 2025-11-04 09:57:53 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			280 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			280 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from collections import OrderedDict
 | 
						|
from django.db import models
 | 
						|
from django.utils.encoding import force_str
 | 
						|
from django.utils.module_loading import import_string
 | 
						|
 | 
						|
from graphene import (
 | 
						|
    ID,
 | 
						|
    Boolean,
 | 
						|
    Dynamic,
 | 
						|
    Enum,
 | 
						|
    Field,
 | 
						|
    Float,
 | 
						|
    Int,
 | 
						|
    List,
 | 
						|
    NonNull,
 | 
						|
    String,
 | 
						|
    UUID,
 | 
						|
    DateTime,
 | 
						|
    Date,
 | 
						|
    Time,
 | 
						|
)
 | 
						|
from graphene.types.json import JSONString
 | 
						|
from graphene.utils.str_converters import to_camel_case, to_const
 | 
						|
from graphql import assert_valid_name
 | 
						|
 | 
						|
from .settings import graphene_settings
 | 
						|
from .compat import ArrayField, HStoreField, JSONField, RangeField
 | 
						|
from .fields import DjangoListField, DjangoConnectionField
 | 
						|
from .utils import import_single_dispatch
 | 
						|
 | 
						|
singledispatch = import_single_dispatch()
 | 
						|
 | 
						|
 | 
						|
def convert_choice_name(name):
 | 
						|
    name = to_const(force_str(name))
 | 
						|
    try:
 | 
						|
        assert_valid_name(name)
 | 
						|
    except AssertionError:
 | 
						|
        name = "A_%s" % name
 | 
						|
    return name
 | 
						|
 | 
						|
 | 
						|
def get_choices(choices):
 | 
						|
    converted_names = []
 | 
						|
    if isinstance(choices, OrderedDict):
 | 
						|
        choices = choices.items()
 | 
						|
    for value, help_text in choices:
 | 
						|
        if isinstance(help_text, (tuple, list)):
 | 
						|
            for choice in get_choices(help_text):
 | 
						|
                yield choice
 | 
						|
        else:
 | 
						|
            name = convert_choice_name(value)
 | 
						|
            while name in converted_names:
 | 
						|
                name += "_" + str(len(converted_names))
 | 
						|
            converted_names.append(name)
 | 
						|
            description = help_text
 | 
						|
            yield name, value, description
 | 
						|
 | 
						|
 | 
						|
def convert_choices_to_named_enum_with_descriptions(name, choices):
 | 
						|
    choices = list(get_choices(choices))
 | 
						|
    named_choices = [(c[0], c[1]) for c in choices]
 | 
						|
    named_choices_descriptions = {c[0]: c[2] for c in choices}
 | 
						|
 | 
						|
    class EnumWithDescriptionsType(object):
 | 
						|
        @property
 | 
						|
        def description(self):
 | 
						|
            return named_choices_descriptions[self.name]
 | 
						|
 | 
						|
    return Enum(name, list(named_choices), type=EnumWithDescriptionsType)
 | 
						|
 | 
						|
 | 
						|
def generate_enum_name(django_model_meta, field):
 | 
						|
    if graphene_settings.DJANGO_CHOICE_FIELD_ENUM_CUSTOM_NAME:
 | 
						|
        # Try and import custom function
 | 
						|
        custom_func = import_string(
 | 
						|
            graphene_settings.DJANGO_CHOICE_FIELD_ENUM_CUSTOM_NAME
 | 
						|
        )
 | 
						|
        name = custom_func(field)
 | 
						|
    elif graphene_settings.DJANGO_CHOICE_FIELD_ENUM_V3_NAMING is True:
 | 
						|
        name = "{app_label}{object_name}{field_name}Choices".format(
 | 
						|
            app_label=to_camel_case(django_model_meta.app_label.title()),
 | 
						|
            object_name=django_model_meta.object_name,
 | 
						|
            field_name=to_camel_case(field.name.title()),
 | 
						|
        )
 | 
						|
    else:
 | 
						|
        name = to_camel_case("{}_{}".format(django_model_meta.object_name, field.name))
 | 
						|
    return name
 | 
						|
 | 
						|
 | 
						|
def convert_choice_field_to_enum(field, name=None):
 | 
						|
    if name is None:
 | 
						|
        name = generate_enum_name(field.model._meta, field)
 | 
						|
    choices = field.choices
 | 
						|
    return convert_choices_to_named_enum_with_descriptions(name, choices)
 | 
						|
 | 
						|
 | 
						|
def convert_django_field_with_choices(
 | 
						|
    field, registry=None, convert_choices_to_enum=True
 | 
						|
):
 | 
						|
    if registry is not None:
 | 
						|
        converted = registry.get_converted_field(field)
 | 
						|
        if converted:
 | 
						|
            return converted
 | 
						|
    choices = getattr(field, "choices", None)
 | 
						|
    if choices and convert_choices_to_enum:
 | 
						|
        enum = convert_choice_field_to_enum(field)
 | 
						|
        required = not (field.blank or field.null)
 | 
						|
        converted = enum(description=field.help_text, required=required)
 | 
						|
    else:
 | 
						|
        converted = convert_django_field(field, registry)
 | 
						|
    if registry is not None:
 | 
						|
        registry.register_converted_field(field, converted)
 | 
						|
    return converted
 | 
						|
 | 
						|
 | 
						|
@singledispatch
 | 
						|
def convert_django_field(field, registry=None):
 | 
						|
    raise Exception(
 | 
						|
        "Don't know how to convert the Django field %s (%s)" % (field, field.__class__)
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(models.CharField)
 | 
						|
@convert_django_field.register(models.TextField)
 | 
						|
@convert_django_field.register(models.EmailField)
 | 
						|
@convert_django_field.register(models.SlugField)
 | 
						|
@convert_django_field.register(models.URLField)
 | 
						|
@convert_django_field.register(models.GenericIPAddressField)
 | 
						|
@convert_django_field.register(models.FileField)
 | 
						|
@convert_django_field.register(models.FilePathField)
 | 
						|
def convert_field_to_string(field, registry=None):
 | 
						|
    return String(description=field.help_text, required=not field.null)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(models.AutoField)
 | 
						|
def convert_field_to_id(field, registry=None):
 | 
						|
    return ID(description=field.help_text, required=not field.null)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(models.UUIDField)
 | 
						|
def convert_field_to_uuid(field, registry=None):
 | 
						|
    return UUID(description=field.help_text, required=not field.null)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(models.PositiveIntegerField)
 | 
						|
@convert_django_field.register(models.PositiveSmallIntegerField)
 | 
						|
@convert_django_field.register(models.SmallIntegerField)
 | 
						|
@convert_django_field.register(models.BigIntegerField)
 | 
						|
@convert_django_field.register(models.IntegerField)
 | 
						|
def convert_field_to_int(field, registry=None):
 | 
						|
    return Int(description=field.help_text, required=not field.null)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(models.BooleanField)
 | 
						|
def convert_field_to_boolean(field, registry=None):
 | 
						|
    return NonNull(Boolean, description=field.help_text)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(models.NullBooleanField)
 | 
						|
def convert_field_to_nullboolean(field, registry=None):
 | 
						|
    return Boolean(description=field.help_text, required=not field.null)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(models.DecimalField)
 | 
						|
@convert_django_field.register(models.FloatField)
 | 
						|
@convert_django_field.register(models.DurationField)
 | 
						|
def convert_field_to_float(field, registry=None):
 | 
						|
    return Float(description=field.help_text, required=not field.null)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(models.DateTimeField)
 | 
						|
def convert_datetime_to_string(field, registry=None):
 | 
						|
    return DateTime(description=field.help_text, required=not field.null)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(models.DateField)
 | 
						|
def convert_date_to_string(field, registry=None):
 | 
						|
    return Date(description=field.help_text, required=not field.null)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(models.TimeField)
 | 
						|
def convert_time_to_string(field, registry=None):
 | 
						|
    return Time(description=field.help_text, required=not field.null)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(models.OneToOneRel)
 | 
						|
def convert_onetoone_field_to_djangomodel(field, registry=None):
 | 
						|
    model = field.related_model
 | 
						|
 | 
						|
    def dynamic_type():
 | 
						|
        _type = registry.get_type_for_model(model)
 | 
						|
        if not _type:
 | 
						|
            return
 | 
						|
 | 
						|
        # We do this for a bug in Django 1.8, where null attr
 | 
						|
        # is not available in the OneToOneRel instance
 | 
						|
        null = getattr(field, "null", True)
 | 
						|
        return Field(_type, required=not null)
 | 
						|
 | 
						|
    return Dynamic(dynamic_type)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(models.ManyToManyField)
 | 
						|
@convert_django_field.register(models.ManyToManyRel)
 | 
						|
@convert_django_field.register(models.ManyToOneRel)
 | 
						|
def convert_field_to_list_or_connection(field, registry=None):
 | 
						|
    model = field.related_model
 | 
						|
 | 
						|
    def dynamic_type():
 | 
						|
        _type = registry.get_type_for_model(model)
 | 
						|
        if not _type:
 | 
						|
            return
 | 
						|
 | 
						|
        description = (
 | 
						|
            field.help_text
 | 
						|
            if isinstance(field, models.ManyToManyField)
 | 
						|
            else field.field.help_text
 | 
						|
        )
 | 
						|
 | 
						|
        # If there is a connection, we should transform the field
 | 
						|
        # into a DjangoConnectionField
 | 
						|
        if _type._meta.connection:
 | 
						|
            # Use a DjangoFilterConnectionField if there are
 | 
						|
            # defined filter_fields or a filterset_class in the
 | 
						|
            # DjangoObjectType Meta
 | 
						|
            if _type._meta.filter_fields or _type._meta.filterset_class:
 | 
						|
                from .filter.fields import DjangoFilterConnectionField
 | 
						|
 | 
						|
                return DjangoFilterConnectionField(
 | 
						|
                    _type, required=True, description=description
 | 
						|
                )
 | 
						|
 | 
						|
            return DjangoConnectionField(_type, required=True, description=description)
 | 
						|
 | 
						|
        return DjangoListField(
 | 
						|
            _type,
 | 
						|
            required=True,  # A Set is always returned, never None.
 | 
						|
            description=description,
 | 
						|
        )
 | 
						|
 | 
						|
    return Dynamic(dynamic_type)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(models.OneToOneField)
 | 
						|
@convert_django_field.register(models.ForeignKey)
 | 
						|
def convert_field_to_djangomodel(field, registry=None):
 | 
						|
    model = field.related_model
 | 
						|
 | 
						|
    def dynamic_type():
 | 
						|
        _type = registry.get_type_for_model(model)
 | 
						|
        if not _type:
 | 
						|
            return
 | 
						|
 | 
						|
        return Field(_type, description=field.help_text, required=not field.null)
 | 
						|
 | 
						|
    return Dynamic(dynamic_type)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(ArrayField)
 | 
						|
def convert_postgres_array_to_list(field, registry=None):
 | 
						|
    base_type = convert_django_field(field.base_field)
 | 
						|
    if not isinstance(base_type, (List, NonNull)):
 | 
						|
        base_type = type(base_type)
 | 
						|
    return List(base_type, description=field.help_text, required=not field.null)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(HStoreField)
 | 
						|
@convert_django_field.register(JSONField)
 | 
						|
def convert_postgres_field_to_string(field, registry=None):
 | 
						|
    return JSONString(description=field.help_text, required=not field.null)
 | 
						|
 | 
						|
 | 
						|
@convert_django_field.register(RangeField)
 | 
						|
def convert_postgres_range_to_string(field, registry=None):
 | 
						|
    inner_type = convert_django_field(field.base_field)
 | 
						|
    if not isinstance(inner_type, (List, NonNull)):
 | 
						|
        inner_type = type(inner_type)
 | 
						|
    return List(inner_type, description=field.help_text, required=not field.null)
 |