def test_mutation_meta_registry(self): """Test having registry information stored in mutation meta.""" self.assertEqual(ProjectUpdateMutation._meta.registry, get_global_registry()) self.assertNotEqual(ProjectUpdateMutation._meta.registry, project_name_only_registry) self.assertNotEqual(ProjectNameOnlyUpdateMutation._meta.registry, get_global_registry()) self.assertEqual(ProjectNameOnlyUpdateMutation._meta.registry, project_name_only_registry)
def convert_serializer_field(field, is_input=True, convert_choices_to_enum=True, force_optional=False): """ Converts a django rest frameworks field to a graphql field and marks the field as required if we are creating an input type and the field itself is required """ if isinstance(field, serializers.ChoiceField) and not convert_choices_to_enum: graphql_type = graphene.String elif isinstance(field, serializers.FileField): graphql_type = Upload else: graphql_type = get_graphene_type_from_serializer_field(field) args = [] kwargs = { "description": field.help_text, "required": is_input and field.required and not force_optional } # if it is a tuple or a list it means that we are returning # the graphql type and the child type if isinstance(graphql_type, (list, tuple)): kwargs["of_type"] = graphql_type[1] graphql_type = graphql_type[0] if isinstance(field, serializers.ModelSerializer): if is_input: graphql_type = convert_serializer_to_input_type(field.__class__) else: global_registry = get_global_registry() field_model = field.Meta.model args = [global_registry.get_type_for_model(field_model)] elif isinstance(field, serializers.Serializer): if is_input: graphql_type = convert_serializer_to_input_type(field.__class__) elif isinstance(field, serializers.ListSerializer): field = field.child if is_input: # All the serializer items within the list is considered non-nullable kwargs["of_type"] = graphene.NonNull( convert_serializer_to_input_type(field.__class__)) else: del kwargs["of_type"] global_registry = get_global_registry() field_model = field.Meta.model args = [global_registry.get_type_for_model(field_model)] return graphql_type(*args, **kwargs)
def __init_subclass_with_meta__( cls, model=None, permissions=None, login_required=None, only_fields=(), exclude_fields=(), return_field_name=None, **kwargs, ): registry = get_global_registry() if not return_field_name: return_field_name = to_snake_case(model.__name__) arguments = OrderedDict(id=graphene.ID(required=True)) output_fields = OrderedDict() output_fields["found"] = graphene.Boolean() output_fields["deleted_id"] = graphene.ID() _meta = DjangoDeleteMutationOptions(cls) _meta.model = model _meta.fields = yank_fields_from_attrs(output_fields, _as=graphene.Field) _meta.return_field_name = return_field_name _meta.permissions = permissions _meta.login_required = _meta.login_required or ( _meta.permissions and len(_meta.permissions) > 0) super().__init_subclass_with_meta__(arguments=arguments, _meta=_meta, **kwargs)
def convert_field(field): registry = get_global_registry() converted = registry.get_converted_field(field) if converted: return converted _filter_coll = filterset_class() def _get_or_make_field(name, filt): return convert_form_field(filt.field) def _should_include_filter(filt): # if we're in ordering mode, we want # to return True for all CalumaOrdering types, # and if it's false, we want the opposite return ordering == isinstance(filt, CalumaOrdering) filter_fields = { name: _get_or_make_field(name, filt) for name, filt in _filter_coll.filters.items() # exclude orderBy in our fields. We want only new-style order filters if _should_include_filter(filt) and name != "orderBy" } if ordering: filter_fields["direction"] = AscDesc(default=AscDesc.ASC, required=False) else: filter_fields["invert"] = graphene.Boolean(required=False, default=False) filter_type = type(field_type_name, (InputObjectType,), filter_fields) converted = List(filter_type) registry.register_converted_field(field, converted) return converted
def test_choices_field__choice_field_in_different_locations__accepts_different_parameters(self): class MockModel(models.Model): field_with_choices = models.CharField( max_length=16, choices=( ("A", "Choice a"), ("B", "Choice b"), ("C", "Choice c"), ) ) registry = get_global_registry() field = MockModel._meta.get_field("field_with_choices") result = convert_django_field_with_choices( field, registry=registry ) self.assertIsInstance(result, graphene.types.Enum) self.assertEqual(result.kwargs.get("required"), True) result = convert_django_field_with_choices( field, registry, required=False ) self.assertIsInstance(result, graphene.types.Enum) self.assertEqual(result.kwargs.get("required"), False)
def test__one_to_one_field__is_properly_converted(self): class MockSuperModel(models.Model): name = models.CharField(max_length=128, default="Heidi Klum") class MockModel(models.Model): super_model = models.OneToOneField(MockSuperModel, on_delete=models.CASCADE, related_name='modelling') # Model-ling... Get it? xD # Convert primary relation registry = get_global_registry() field = MockModel._meta.get_field("super_model") result = convert_django_field_with_choices( field, registry=registry ) self.assertIsInstance(result, graphene.types.ID) self.assertEqual(result.kwargs.get("required"), True) # Convert backward relation field = MockSuperModel._meta.get_field("modelling") result = convert_django_field_with_choices( field, registry=registry ) self.assertIsInstance(result, graphene.types.ID)
def convert_choice_field_to_enum(field): """ Add support to convert ordering choices to Graphql enum. Label is used as enum name. """ registry = get_global_registry() converted = registry.get_converted_field(field) if converted: return converted def get_choices(choices): for value, help_text in choices: if value: name = convert_choice_name(value) description = help_text yield name, value, description name = to_camel_case(field.label) choices = list(get_choices(field.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] enum = Enum(name, list(named_choices), type=EnumWithDescriptionsType) converted = enum(description=field.help_text, required=field.required) registry.register_converted_field(field, converted) return converted
def get_output_fields(model, return_field_name): """Return mutation output field for model instance.""" model_type = get_global_registry().get_type_for_model(model) if not model_type: raise ImproperlyConfigured( "Unable to find type for model %s in graphene registry" % model.__name__ ) fields = {return_field_name: Field(model_type)} return fields
def convert_meta_value_field(field): registry = get_global_registry() converted = registry.get_converted_field(field) if converted: return converted converted = List(SearchAnswersFilterType) registry.register_converted_field(field, converted) return converted
def __init_subclass_with_meta__( cls, form_class=None, model=None, fields=ALL_FIELDS, permission_classes=None, return_field_name=None, deleting=False, is_relay=False, **options ): if form_class and not model: model = form_class._meta.model if not model: raise Exception("model is required for DjangoModelMutation") if not form_class: form_class = model_forms.modelform_factory(model, fields=fields) input_fields = {} if not deleting: form = form_class() input_fields = fields_for_form(form, fields) if fields == ALL_FIELDS or "id" in fields or deleting: if is_relay: input_fields["id"] = graphene.GlobalID() else: input_fields["id"] = graphene.ID() registry = get_global_registry() model_type = registry.get_type_for_model(model) if not model_type: raise Exception("No type registered for model: {}".format(model.__name__)) if not return_field_name: model_name = model.__name__ return_field_name = model_name[:1].lower() + model_name[1:] output_fields = OrderedDict() output_fields[return_field_name] = graphene.Field(model_type) _meta = DjangoModelMutationOptions(cls) _meta.form_class = form_class _meta.fields = fields _meta.model = model _meta.permission_classes = permission_classes or () _meta.return_field_name = return_field_name _meta.deleting = deleting _meta.is_relay = is_relay _meta.fields = yank_fields_from_attrs(output_fields, _as=Field) input_fields = yank_fields_from_attrs(input_fields, _as=InputField) super(DjangoModelMutation, cls).__init_subclass_with_meta__( _meta=_meta, input_fields=input_fields, **options )
def convert_meta_value_field(field): registry = get_global_registry() converted = registry.get_converted_field(field) if converted: return converted converted = MetaValueFilterType() registry.register_converted_field(field, converted) return converted
def schema_for_field(field, name, registry=None): registry = registry or get_global_registry() s = get_field_schema(field, registry) default_value = getattr(field, "default", None) if default_value is NOT_PROVIDED: default_value = None if default_value is not None and callable(default_value): default_value = default_value() if default_value is not None: if isinstance(default_value, decimal.Decimal): default_value = str(default_value) if isinstance(default_value, (datetime.datetime, datetime.date, datetime.time)): default_value = default_value.isoformat() if isinstance(field, (ManyToOneRel, ManyToManyRel)): required = not field.null else: required = not field.blank and field.default is NOT_PROVIDED items = getattr(field, "choices", None) if items: if isinstance(items, dict): # pragma:nocover items = items.items() choices = [] for (_original_v, label), (n, _value, _desc) in zip(items, get_choices(items)): choices.append( { "label": label, "value": n, } ) else: choices = None s = update_dict_nested( s, { "name": to_camel_case(name), # FIXME: Get verbose_name and help_text for m2m "label": getattr(field, "verbose_name", None), "help_text": getattr(field, "help_text", None), "hidden": name == "id", "choices": choices, "default_value": default_value, "validation": { "required": required, "min_length": getattr(field, "min_length", None), "max_length": getattr(field, "max_length", None), "min_value": None, "max_value": None, }, }, ) return s
def mutate(cls, root, info, ids): cls.before_mutate(root, info, ids) if cls._meta.login_required and not info.context.user.is_authenticated: raise GraphQLError("Must be logged in to access this mutation.") cls.check_permissions(root, info, ids) Model = cls._meta.model ids = cls.resolve_ids(ids) cls.validate(root, info, ids) qs_to_delete = cls.get_queryset(root, info, ids).filter(id__in=ids) updated_qs = cls.before_save(root, info, ids, qs_to_delete) if updated_qs: qs_to_delete = updated_qs # Find out which (global) ids are deleted, and which were not found. deleted_ids = [ to_global_id( get_global_registry().get_type_for_model(Model).__name__, id) for id in qs_to_delete.values_list("id", flat=True) ] all_global_ids = [ to_global_id( get_global_registry().get_type_for_model(Model).__name__, id) for id in ids ] missed_ids = list(set(all_global_ids).difference(deleted_ids)) deletion_count, _ = qs_to_delete.delete() cls.after_mutate(root, info, ids, deletion_count, deleted_ids) return cls( deletion_count=deletion_count, deleted_ids=deleted_ids, missed_ids=missed_ids, )
def convert_meta_value_field(field): registry = get_global_registry() converted = registry.get_converted_field(field) if converted: return converted # the converted type must be list-of-filter, as we need to apply # multiple conditions converted = List(HasAnswerFilterType) registry.register_converted_field(field, converted) return converted
def to_enum(field): registry = get_global_registry() converted = registry.get_converted_field(field) if converted: # pragma: no cover return converted converted = field_enum() registry.register_converted_field(field, converted) return converted
def convert_meta_value_field(field): registry = get_global_registry() converted = registry.get_converted_field(field) if converted: # TODO: can this be removed, as it's never the case # anymore with graphene 3.0? # Doesn't seem to have ill effects... return converted # pragma: no cover converted = List(SearchAnswersFilterType) registry.register_converted_field(field, converted) return converted
def _optimize_field_by_name(self, store, model, selection, field_def): name = self._get_name_from_resolver(field_def.resolve) if not name: return False full_name = name # "split" here for deep model_field resolver hint if it's not partial split_model_field = callable(name) and [name] or full_name.split( LOOKUP_SEP, 1) name = split_model_field[0] model_field = self._get_model_field_from_name(model, name) if not model_field: return False if self._is_foreign_key_id(model_field, name) or not model_field.is_relation: store.only(name) return True field_type = self._get_type(field_def) selection_next = selection # if we're dealing with a deep model_field resolver hint, # get the type of the first link in the chain if len(split_model_field) > 1: model_next = model_field.related_model model_next_type = get_global_registry().get_type_for_model( model_next) field_type = GrapheneObjectType(graphene_type=model_next_type, name=model_next_type._meta.name, fields=field_type._fields) # we're optimizing a deep model_field resolver hint, # so introduce synthetic parents in the ast to prompt # the optimizer selection_next = self._insert_selection_set_parent_nodes_from_deep_field_name( selection, full_name) field_store = self._optimize_gql_selections(field_type, selection_next) if model_field.many_to_one or model_field.one_to_one: store.select_related(name, field_store) return True if model_field.one_to_many or model_field.many_to_many: if isinstance(model_field, ManyToOneRel): field_store.only(model_field.field.name) related_queryset = model_field.related_model.objects.all() store.prefetch_related(name, field_store, related_queryset) return True return False
def convert_serializer_field_to_enum(field): # TODO: could be removed once following issue is fixed # https://github.com/graphql-python/graphene-django/issues/517 model_class = None serializer_meta = getattr(field.parent, "Meta", None) if serializer_meta: model_class = getattr(serializer_meta, "model", None) if model_class: registry = get_global_registry() model_field = model_class._meta.get_field(field.source) return type(convert_django_field_with_choices(model_field, registry)) return graphene.String
def __init_subclass_with_meta__(cls, form_class=None, model=None, return_field_name=None, only_fields=(), exclude_fields=(), **options): if not form_class: raise Exception( 'form_class is required for DjangoModelFormMutation') if not model: model = form_class._meta.model if not model: raise Exception('model is required for DjangoModelFormMutation') form = form_class() input_fields = fields_for_form(form, only_fields, exclude_fields) input_fields['id'] = graphene.ID() registry = get_global_registry() model_type = registry.get_type_for_model(model) return_field_name = return_field_name if not return_field_name: model_name = model.__name__ return_field_name = model_name[:1].lower() + model_name[1:] output_fields = OrderedDict() output_fields[return_field_name] = graphene.Field(model_type) _meta = DjangoModelDjangoFormMutationOptions(cls) _meta.form_class = form_class _meta.model = model _meta.return_field_name = return_field_name _meta.fields = yank_fields_from_attrs( output_fields, _as=Field, ) input_fields = yank_fields_from_attrs( input_fields, _as=InputField, ) super(DjangoModelFormMutation, cls).__init_subclass_with_meta__(_meta=_meta, input_fields=input_fields, **options)
def __init_subclass_with_meta__( cls, _meta=None, model=None, permissions=None, login_required=None, filter_fields=(), filter_class=None, **kwargs, ): registry = get_global_registry() model_type = registry.get_type_for_model(model) assert model_type, f"Model type must be registered for model {model}" assert ( len(filter_fields) > 0 ), f"You must specify at least one field to filter on for deletion." input_arguments = get_filter_fields_input_args(filter_fields, model) InputType = type(f"BatchDelete{model.__name__}Input", (InputObjectType, ), input_arguments) arguments = OrderedDict(input=InputType(required=True)) output_fields = OrderedDict() output_fields["deletion_count"] = graphene.Int() output_fields["deleted_ids"] = graphene.List(graphene.ID) if _meta is None: _meta = DjangoFilterDeleteMutationOptions(cls) _meta.model = model _meta.fields = yank_fields_from_attrs(output_fields, _as=graphene.Field) _meta.filter_fields = filter_fields _meta.permissions = permissions _meta.login_required = login_required or (_meta.permissions and len(_meta.permissions) > 0) super().__init_subclass_with_meta__(arguments=arguments, _meta=_meta, **kwargs)
def mutate(cls, root, info, input): updated_input = cls.before_mutate(root, info, input) if updated_input: input = updated_input if cls._meta.login_required and not info.context.user.is_authenticated: raise GraphQLError("Must be logged in to access this mutation.") cls.check_permissions(root, info, input) Model = cls._meta.model model_field_values = {} for name, value in super(type(input), input).items(): filter_field_split = name.split("__", 1) field_name = filter_field_split[0] try: field = Model._meta.get_field(field_name) except FieldDoesNotExist: # This can happen with nested selectors. In this case we set the field to none. field = None filter_field_is_list = False if len(filter_field_split) > 1: # If we have an "__in" final part of the filter, we are now dealing with # a list of things. Note that all other variants can be coerced directly # on the filter-call, so we don't really have to deal with other cases. filter_field_is_list = filter_field_split[-1] == "in" new_value = value value_handle_name = "handle_" + name if hasattr(cls, value_handle_name): handle_func = getattr(cls, value_handle_name) assert callable( handle_func ), f"Property {value_handle_name} on {cls.__name__} is not a function." new_value = handle_func(value, name, info) # On some fields we perform some default conversion, if the value was not transformed above. if new_value == value and value is not None: if type(field) in (models.ForeignKey, models.OneToOneField): name = getattr(field, "db_column", None) or name + "_id" new_value = cls.resolve_id(value) elif (type(field) in ( models.ManyToManyField, models.ManyToManyRel, models.ManyToOneRel, ) or filter_field_is_list): new_value = cls.resolve_ids(value) model_field_values[name] = new_value filter_qs = cls.get_queryset(root, info, input).filter(**model_field_values) updated_qs = cls.before_save(root, info, filter_qs) if updated_qs: filter_qs = updated_qs ids = [ to_global_id( get_global_registry().get_type_for_model(Model).__name__, id) for id in filter_qs.values_list("id", flat=True) ] deletion_count, _ = filter_qs.delete() cls.after_mutate(root, info, input, deletion_count, ids) return cls(deletion_count=deletion_count, deleted_ids=ids)
def __init_subclass_with_meta__( cls, model=None, permissions=None, login_required=None, only_fields=(), exclude_fields=(), return_field_name=None, auto_context_fields={}, many_to_one_extras=None, many_to_many_extras=None, foreign_key_extras=None, type_name=None, **kwargs, ): registry = get_global_registry() meta_registry = get_type_meta_registry() model_type = registry.get_type_for_model(model) assert model_type, f"Model type must be registered for model {model}" if not return_field_name: return_field_name = to_snake_case(model.__name__) if many_to_one_extras is None: many_to_one_extras = {} if foreign_key_extras is None: foreign_key_extras = {} if many_to_many_extras is None: many_to_many_extras = {} input_type_name = type_name or f"Patch{model.__name__}Input" model_fields = get_all_optional_input_fields_for_model( model, only_fields, exclude_fields, many_to_many_extras=many_to_many_extras, foreign_key_extras=foreign_key_extras, many_to_one_extras=many_to_one_extras, parent_type_name=type_name, ) InputType = type(input_type_name, (InputObjectType, ), model_fields) # Register meta-data meta_registry.register( input_type_name, { 'auto_context_fields': auto_context_fields or {}, 'many_to_many_extras': many_to_many_extras or {}, 'many_to_one_extras': many_to_one_extras or {}, 'foreign_key_extras': foreign_key_extras or {} }) registry.register_converted_field(input_type_name, InputType) arguments = OrderedDict(id=graphene.ID(required=True), input=InputType(required=True)) output_fields = OrderedDict() output_fields[return_field_name] = graphene.Field(model_type) _meta = DjangoPatchMutationOptions(cls) _meta.model = model _meta.fields = yank_fields_from_attrs(output_fields, _as=graphene.Field) _meta.return_field_name = return_field_name _meta.permissions = permissions _meta.auto_context_fields = auto_context_fields or {} _meta.InputType = InputType _meta.input_type_name = input_type_name _meta.many_to_many_extras = many_to_many_extras _meta.many_to_one_extras = many_to_one_extras _meta.foreign_key_extras = foreign_key_extras _meta.login_required = _meta.login_required or ( _meta.permissions and len(_meta.permissions) > 0) super().__init_subclass_with_meta__(arguments=arguments, _meta=_meta, **kwargs)
from itertools import chain import graphene from django.core.exceptions import ImproperlyConfigured, ValidationError from graphene.types.mutation import MutationOptions from graphene_django.registry import get_global_registry from graphql_jwt import ObtainJSONWebToken, Verify from graphql_jwt.exceptions import GraphQLJWTError, PermissionDenied from ...account import models from ..account.types import User from ..file_upload.types import Upload from ..utils import get_node, get_nodes from .types import Error registry = get_global_registry() def get_model_name(model): """Return name of the model with first letter lowercase.""" model_name = model.__name__ return model_name[:1].lower() + model_name[1:] def get_output_fields(model, return_field_name): """Return mutation output field for model instance.""" model_type = registry.get_type_for_model(model) fields = {return_field_name: graphene.Field(model_type)} return fields
def __init_subclass_with_meta__( cls, _meta=None, model=None, permissions=None, login_required=None, filter_fields=(), filter_class=None, type_name=None, only_fields=(), exclude_fields=(), optional_fields=None, required_fields=(), field_types=None, auto_context_fields={}, **kwargs, ): registry = get_global_registry() model_type = registry.get_type_for_model(model) if optional_fields is None: optional_fields = tuple(name for name, _ in get_model_fields(model)) assert model_type, f"Model type must be registered for model {model}" assert ( len(filter_fields) > 0 ), f"You must specify at least one field to filter on for deletion." input_arguments = get_filter_fields_input_args(filter_fields, model) FilterInputType = type( f"FilterUpdate{model.__name__}FilterInput", (InputObjectType, ), input_arguments, ) input_type_name = type_name or f"FilterUpdate{model.__name__}DataInput" model_fields = get_input_fields_for_model( model, only_fields, exclude_fields, tuple(auto_context_fields.keys()) + optional_fields, required_fields, None, None, None, one_to_one_extras=None, parent_type_name=input_type_name, field_types=field_types, ignore_primary_key=True, ) DataInputType = type(input_type_name, (InputObjectType, ), model_fields) # Register meta-data meta_registry.register( input_type_name, { "auto_context_fields": auto_context_fields or {}, "optional_fields": optional_fields, "required_fields": required_fields, "many_to_many_extras": {}, "many_to_one_extras": {}, "foreign_key_extras": {}, "one_to_one_extras": {}, "field_types": field_types or {}, }, ) registry.register_converted_field(input_type_name, DataInputType) arguments = OrderedDict(filter=FilterInputType(required=True), data=DataInputType(required=True)) output_fields = OrderedDict() output_fields["updated_count"] = graphene.Int() output_fields["updated_objects"] = graphene.List(model_type) if _meta is None: _meta = DjangoFilterUpdateMutationOptions(cls) _meta.model = model _meta.fields = yank_fields_from_attrs(output_fields, _as=graphene.Field) _meta.filter_fields = filter_fields _meta.permissions = permissions _meta.login_required = login_required or (_meta.permissions and len(_meta.permissions) > 0) super().__init_subclass_with_meta__(arguments=arguments, _meta=_meta, **kwargs)
def __init_subclass_with_meta__( cls, model=None, permissions=None, login_required=None, only_fields=(), exclude_fields=(), optional_fields=(), required_fields=(), auto_context_fields={}, return_field_name=None, many_to_many_extras=None, foreign_key_extras=None, many_to_one_extras=None, type_name=None, use_type_name=None, **kwargs, ): registry = get_global_registry() meta_registry = get_type_meta_registry() model_type = registry.get_type_for_model(model) if many_to_one_extras is None: many_to_one_extras = {} if foreign_key_extras is None: foreign_key_extras = {} if many_to_many_extras is None: many_to_many_extras = {} assert model_type, f"Model type must be registered for model {model}" if not return_field_name: # Pluralize return_field_name = to_snake_case(model.__name__) + "s" if use_type_name: input_type_name = use_type_name InputType = registry.get_converted_field(input_type_name) if not InputType: raise GraphQLError( f"Could not find input type with name {input_type_name}") else: input_type_name = type_name or f"BatchCreate{model.__name__}Input" model_fields = get_input_fields_for_model( model, only_fields, exclude_fields, tuple(auto_context_fields.keys()) + optional_fields, required_fields, many_to_many_extras, foreign_key_extras, many_to_one_extras, parent_type_name=input_type_name, ) InputType = type(input_type_name, (InputObjectType, ), model_fields) # Register meta-data meta_registry.register( input_type_name, { 'auto_context_fields': auto_context_fields or {}, 'optional_fields': optional_fields, 'required_fields': required_fields, 'many_to_many_extras': many_to_many_extras or {}, 'foreign_key_extras': foreign_key_extras or {} }) registry.register_converted_field(input_type_name, InputType) arguments = OrderedDict(input=graphene.List(InputType, required=True)) output_fields = OrderedDict() output_fields[return_field_name] = graphene.List(model_type) _meta = DjangoBatchCreateMutationOptions(cls) _meta.model = model _meta.fields = yank_fields_from_attrs(output_fields, _as=graphene.Field) _meta.return_field_name = return_field_name _meta.optional_fields = optional_fields _meta.required_fields = required_fields _meta.permissions = permissions _meta.auto_context_fields = auto_context_fields or {} _meta.many_to_many_extras = many_to_many_extras or {} _meta.foreign_key_extras = foreign_key_extras _meta.many_to_one_extras = many_to_one_extras or {} _meta.InputType = InputType _meta.input_type_name = input_type_name _meta.login_required = _meta.login_required or ( _meta.permissions and len(_meta.permissions) > 0) super().__init_subclass_with_meta__(arguments=arguments, _meta=_meta, **kwargs)
def get_input_fields_for_model( model, only_fields, exclude_fields, optional_fields=(), required_fields=(), many_to_many_extras=None, foreign_key_extras=None, many_to_one_extras=None, parent_type_name="", field_types=None, ignore_primary_key=True, ) -> OrderedDict: registry = get_global_registry() meta_registry = get_type_meta_registry() model_fields = get_model_fields(model) many_to_many_extras = many_to_many_extras or {} foreign_key_extras = foreign_key_extras or {} many_to_one_extras = many_to_one_extras or {} field_types = field_types or {} fields = OrderedDict() fields_lookup = {} for name, field in model_fields: # We ignore the primary key if getattr(field, "primary_key", False) and ignore_primary_key: continue # If the field has an override, use that if name in field_types: fields[name] = field_types[name] continue # Save for later fields_lookup[name] = field is_not_in_only = only_fields and name not in only_fields # is_already_created = name in options.fields is_excluded = 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: # We skip this field if we specify only_fields and is not # in there. Or when we exclude this field in exclude_fields. # Or when there is no back reference. continue required = None if name in optional_fields: required = False elif name in required_fields: required = True converted = convert_django_field_with_choices( field, registry, required, many_to_many_extras.get(name, {}).get("exact"), foreign_key_extras.get(name, {}), ) fields[name] = converted # Create extra many_to_many_fields for name, extras in many_to_many_extras.items(): field = fields_lookup.get(name) if field is None: raise GraphQLError( f"Error adding extras for {name} in model f{model}. Field {name} does not exist." ) for extra_name, data in extras.items(): # This is handled above if extra_name == "exact": continue if isinstance(data, bool): data = {} _type_name = data.get("type") _field = convert_many_to_many_field(field, registry, False, data, None) # Default to the same as the "exact" version if not _field: _field = fields[name] # operation = data.get('operation') or get_likely_operation_from_name(extra_name) fields[name + "_" + extra_name] = _field for name, extras in many_to_one_extras.items(): field = fields_lookup.get(name) if field is None: raise GraphQLError( f"Error adding extras for {name} in model f{model}. Field {name} does not exist." ) for extra_name, data in extras.items(): argument_name = data.get("name", name + "_" + extra_name) # Override default if extra_name == "exact": argument_name = name if isinstance(data, bool): data = {"type": "ID"} _type = data.get("type") if not _type or _type == "auto": # Create new type. operation_name = data.get( "operation", get_likely_operation_from_name(extra_name)) _type_name = data.get( "type_name", f"{parent_type_name or ''}{operation_name.capitalize()}{model.__name__}{name.capitalize()}", ) converted_fields = get_input_fields_for_model( field.related_model, data.get("only_fields", ()), data.get( "exclude_fields", (field.field.name, ) ), # Exclude the field referring back to the foreign key data.get("optional_fields", ()), data.get("required_fields", ()), data.get("many_to_many_extras"), data.get("foreign_key_extras"), data.get("many_to_one_extras"), parent_type_name=_type_name, field_types=data.get("field_types"), # Don't ignore the primary key on updates ignore_primary_key=operation_name != "update") InputType = type(_type_name, (InputObjectType, ), converted_fields) meta_registry.register( _type_name, { "auto_context_fields": data.get( "auto_context_fields", {}), "optional_fields": data.get("optional_fields", ()), "required_fields": data.get("required_fields", ()), "many_to_many_extras": data.get( "many_to_many_extras", {}), "many_to_one_extras": data.get("many_to_one_extras", {}), "foreign_key_extras": data.get("auto_context_fields", {}), "field_types": data.get("field_types", {}), }, ) _field = graphene.List( type(_type_name, (InputObjectType, ), converted_fields), required=False, ) else: _field = convert_many_to_many_field(field, registry, False, data, None) fields[argument_name] = _field return fields
def __init_subclass_with_meta__( cls, _meta=None, model=None, permissions=None, login_required=None, only_fields=(), exclude_fields=(), optional_fields=(), required_fields=(), auto_context_fields={}, return_field_name=None, many_to_many_extras=None, foreign_key_extras=None, many_to_one_extras=None, one_to_one_extras=None, type_name=None, field_types=None, ignore_primary_key=True, custom_fields=None, **kwargs, ): registry = get_global_registry() meta_registry = get_type_meta_registry() model_type = registry.get_type_for_model(model) if auto_context_fields is None: auto_context_fields = {} if many_to_one_extras is None: many_to_one_extras = {} if foreign_key_extras is None: foreign_key_extras = {} if many_to_many_extras is None: many_to_many_extras = {} if one_to_one_extras is None: one_to_one_extras = {} if custom_fields is None: custom_fields = {} assert model_type, f"Model type must be registered for model {model}" if not return_field_name: return_field_name = to_snake_case(model.__name__) input_type_name = type_name or f"Create{model.__name__}Input" model_fields = get_input_fields_for_model( model, only_fields, exclude_fields, tuple(auto_context_fields.keys()) + optional_fields, required_fields, many_to_many_extras, foreign_key_extras, many_to_one_extras, one_to_one_extras=one_to_one_extras, parent_type_name=input_type_name, field_types=field_types, ignore_primary_key=ignore_primary_key, ) for name, field in custom_fields.items(): model_fields[name] = field InputType = type(input_type_name, (InputObjectType, ), model_fields) # Register meta-data meta_registry.register( input_type_name, { "auto_context_fields": auto_context_fields or {}, "optional_fields": optional_fields, "required_fields": required_fields, "many_to_many_extras": many_to_many_extras, "many_to_one_extras": many_to_one_extras, "foreign_key_extras": foreign_key_extras, "one_to_one_extras": one_to_one_extras, "field_types": field_types or {}, }, ) registry.register_converted_field(input_type_name, InputType) arguments = OrderedDict(input=InputType(required=True)) output_fields = OrderedDict() output_fields[return_field_name] = graphene.Field(model_type) if _meta is None: _meta = DjangoCreateMutationOptions(cls) _meta.model = model _meta.fields = yank_fields_from_attrs(output_fields, _as=graphene.Field) _meta.return_field_name = return_field_name _meta.optional_fields = optional_fields _meta.required_fields = required_fields _meta.permissions = permissions _meta.auto_context_fields = auto_context_fields _meta.many_to_many_extras = many_to_many_extras _meta.many_to_one_extras = many_to_one_extras _meta.foreign_key_extras = foreign_key_extras _meta.one_to_one_extras = one_to_one_extras _meta.field_types = field_types or {} _meta.InputType = InputType _meta.input_type_name = input_type_name _meta.login_required = login_required or (_meta.permissions and len(_meta.permissions) > 0) super().__init_subclass_with_meta__(arguments=arguments, _meta=_meta, **kwargs)
def __init_subclass_with_meta__( cls, _meta=None, model=None, permissions=None, permissions_any=True, object_permissions=None, object_permissions_any=True, object_permissions_with_superuser=True, fields_schema=None, public=None, only_fields=None, fields=None, exclude_fields=None, exclude=None, **kwargs, ): if not _meta: _meta = ModelTypeOptions(cls) if "allow_unauthenticated" in kwargs: raise ImproperlyConfigured("Use 'public' instead of 'allow_unauthenticated'") _meta.permissions = permissions or [] _meta.permissions_any = permissions_any _meta.object_permissions = object_permissions or [] _meta.object_permissions_any = object_permissions_any _meta.object_permissions_with_superuser = object_permissions_with_superuser _meta.public = public _fields_schema = {} # graphene will handle the deprecated only_fields/exclude_fields for us # We just want to mimic the logic here _include = fields if fields is not None else only_fields if _include is not None: _include = set(_include) _exclude = set(exclude or []) or set(exclude_fields or []) for name, field in get_model_fields(model): if name in _exclude: continue if _include is not None and name not in _include: continue registry = kwargs.get("registry", get_global_registry()) _fields_schema[name] = schema_for_field(field, name, registry) fields_schema = update_dict_nested( _fields_schema, fields_schema or {}, ) _meta.fields_schema = fields_schema or {} super().__init_subclass_with_meta__( _meta=_meta, model=model, only_fields=only_fields, fields=fields, exclude_fields=exclude_fields, exclude=exclude, **kwargs, ) schema_registry[cls._meta.name] = { "object_type": cls._meta.name, "fields": list(_meta.fields_schema.values()), }
def __init_subclass_with_meta__(cls, lookup_field=None, lookup_input_kwarg=None, serializer_class=None, model_class=None, model_operations=["create", "update"], fields=(), exclude=(), return_field_name=None, return_field_type=None, **options): if not serializer_class: raise Exception("serializer_class is required for the Mutation") if "update" not in model_operations and "create" not in model_operations: raise Exception( 'model_operations must contain "create" and/or "update"') serializer = serializer_class() if model_class is None: serializer_meta = getattr(serializer_class, "Meta", None) if serializer_meta: model_class = getattr(serializer_meta, "model", None) if lookup_field is None and model_class: lookup_field = model_class._meta.pk.name if lookup_input_kwarg is None: lookup_input_kwarg = lookup_field input_fields = fields_for_serializer(serializer, fields, exclude, is_input=True) if return_field_name is None: model_name = model_class.__name__ return_field_name = model_name[:1].lower() + model_name[1:] if not return_field_type: registry = get_global_registry() return_field_type = registry.get_type_for_model(model_class) output_fields = OrderedDict() if return_field_name: output_fields[return_field_name] = graphene.Field( return_field_type) _meta = MutationOptions(cls) _meta.lookup_field = lookup_field _meta.lookup_input_kwarg = lookup_input_kwarg _meta.model_operations = model_operations _meta.serializer_class = serializer_class _meta.model_class = model_class _meta.fields = yank_fields_from_attrs(output_fields, _as=Field) _meta.return_field_name = return_field_name _meta.return_field_type = return_field_type input_fields = yank_fields_from_attrs(input_fields, _as=InputField) super().__init_subclass_with_meta__(_meta=_meta, input_fields=input_fields, **options)
from django.db.models.fields.files import FileField from graphene import ObjectType from graphene.types.mutation import MutationOptions from graphene_django.registry import get_global_registry from graphql.error import GraphQLError from graphql_jwt import ObtainJSONWebToken, Verify from graphql_jwt.exceptions import JSONWebTokenError, PermissionDenied from ...account import models from ..account.types import User from ..utils import get_nodes from .types import Error, MetaInput, MetaPath, Upload from .utils import from_global_id_strict_type, snake_to_camel_case from .utils.error_codes import get_error_code_from_error registry = get_global_registry() def get_model_name(model): """Return name of the model with first letter lowercase.""" model_name = model.__name__ return model_name[:1].lower() + model_name[1:] def get_output_fields(model, return_field_name): """Return mutation output field for model instance.""" model_type = registry.get_type_for_model(model) if not model_type: raise ImproperlyConfigured( "Unable to find type for model %s in graphene registry" % model.__name__ )
def get_input_fields_for_model( model, only_fields, exclude_fields, optional_fields=(), required_fields=(), many_to_many_extras=None, foreign_key_extras=None, many_to_one_extras=None, one_to_one_extras=None, parent_type_name="", field_types=None, ignore_primary_key=True, ) -> OrderedDict: registry = get_global_registry() meta_registry = get_type_meta_registry() model_fields = get_model_fields(model) many_to_many_extras = resolve_many_to_many_extra_auto_field_names( many_to_many_extras or {}, model, parent_type_name) many_to_one_extras = resolve_many_to_one_extra_auto_field_names( many_to_one_extras or {}, model, parent_type_name) foreign_key_extras = resolve_foreign_key_extra_auto_field_names( foreign_key_extras or {}, model, parent_type_name) one_to_one_extras = resolve_one_to_one_extra_auto_field_names( one_to_one_extras or {}, model, parent_type_name) field_types = field_types or {} one_to_one_fields: List[Union[models.OneToOneRel, models.OneToOneField]] = [] fields = OrderedDict() fields_lookup = {} for name, field in model_fields: # We ignore the primary key if getattr(field, "primary_key", False) and ignore_primary_key: continue # If the field has an override, use that if name in field_types: fields[name] = field_types[name] continue # Save for later fields_lookup[name] = field is_not_in_only = only_fields and name not in only_fields # is_already_created = name in options.fields is_excluded = 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: # We skip this field if we specify only_fields and is not # in there. Or when we exclude this field in exclude_fields. # Or when there is no back reference. continue required = None if name in optional_fields: required = False elif name in required_fields: required = True converted = convert_django_field_with_choices( field, registry, required, many_to_many_extras.get(name, {}).get("exact"), foreign_key_extras.get(name, {}), one_to_one_extras.get(name, {}), ) fields[name] = converted if type(field) in (models.OneToOneRel, models.OneToOneField): one_to_one_fields.append(field) # Create the one to one field types here. for name, data in one_to_one_extras.items(): field: Union[models.OneToOneRel, models.OneToOneField] = fields_lookup.get(name) if field is None: raise ValueError( f"Error adding extras for {name} in model f{model}. Field {name} does not exist." ) type_name = data.get("type") if type_name == "ID": continue # On OneToOnerels we can get the reverse field name from "field.field.name", as we have a direct # reference to the reverse field that way. For OneToOneFields we need to go through "field.target_field". reverse_field_name = (field.field.name if isinstance( field, models.OneToOneRel) else field.remote_field.name) converted_fields = get_input_fields_for_model( field.related_model, data.get("only_fields", ()), data.get( "exclude_fields", (reverse_field_name, )), # Exclude the field referring back to the foreign key data.get("optional_fields", ()), data.get("required_fields", ()), data.get("many_to_many_extras"), data.get("foreign_key_extras"), data.get("many_to_one_extras"), parent_type_name=type_name, field_types=data.get("field_types"), ) InputType = type(type_name, (InputObjectType, ), converted_fields) registry.register_converted_field(type_name, InputType) meta_registry.register(type_name, data) # Create extra many_to_many_fields for name, extras in many_to_many_extras.items(): field: Union[models.ManyToManyField, models.ManyToManyRel] = fields_lookup.get(name) if field is None: raise GraphQLError( f"Error adding extras for {name} in model f{model}. Field {name} does not exist." ) for extra_name, data in extras.items(): # This is handled above if extra_name == "exact": continue if isinstance(data, bool): data = {} _type_name = data.get("type") _field = convert_many_to_many_field(field, registry, False, data, None) # Default to the same as the "exact" version if not _field: _field = fields[name] # operation = data.get('operation') or get_likely_operation_from_name(extra_name) fields[name + "_" + extra_name] = _field for name, extras in many_to_one_extras.items(): field: models.ManyToOneRel = fields_lookup.get(name) if field is None: raise GraphQLError( f"Error adding extras for {name} in model f{model}. Field {name} does not exist." ) for extra_name, data in extras.items(): argument_name = data.get("name", name + "_" + extra_name) # Override default if extra_name == "exact": argument_name = name if isinstance(data, bool): data = {"type": "ID"} type_name = data.get("type") if type_name and type_name != "ID": # Create new type. operation_name = data.get( "operation", get_likely_operation_from_name(extra_name)) converted_fields = get_input_fields_for_model( field.related_model, data.get("only_fields", ()), data.get( "exclude_fields", (field.field.name, ) ), # Exclude the field referring back to the foreign key data.get("optional_fields", ()), data.get("required_fields", ()), data.get("many_to_many_extras"), data.get("foreign_key_extras"), data.get("many_to_one_extras"), data.get("one_to_one_extras"), parent_type_name=type_name, field_types=data.get("field_types"), # Don't ignore the primary key on updates ignore_primary_key=operation_name != "update", ) InputType = type(type_name, (InputObjectType, ), converted_fields) registry.register_converted_field(field, InputType) meta_registry.register( type_name, { "auto_context_fields": data.get( "auto_context_fields", {}), "optional_fields": data.get("optional_fields", ()), "required_fields": data.get("required_fields", ()), "many_to_many_extras": data.get( "many_to_many_extras", {}), "many_to_one_extras": data.get("many_to_one_extras", {}), "foreign_key_extras": data.get("auto_context_fields", {}), "one_to_one_extras": data.get("one_to_one_extras", {}), "field_types": data.get("field_types", {}), }, ) registry.register_converted_field(type_name, InputType) _field = graphene.List( type(type_name, (InputObjectType, ), converted_fields), required=False, ) else: _field = convert_many_to_many_field(field, registry, False, data, None) fields[argument_name] = _field return fields