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 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)
Beispiel #3
0
def _convert_filter_field(filter_field, model):
    filter_field_split = filter_field.split("__")
    field_name = filter_field_split[0]
    model_field = model._meta.get_field(field_name)

    filter_field_is_list = False
    # In this case, we have a deeply nested field. To find the correct field, we recurse into the string
    if len(filter_field_split) > 2:
        return _convert_filter_field(
            "__".join(filter_field_split[1:]),
            model_field.related_model,  # This fails only on bad input
        )

    if len(filter_field_split) == 2:
        # 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 here.
        if filter_field_split[1] == "in":
            filter_field_is_list = True
        elif model_field.related_model is not None:
            # Check if the field has a related model. If it does, we recurse one last time, otherwise
            # we are dealing with the final field and some filter, e.g. fieldname__contains.
            return _convert_filter_field(
                "__".join(filter_field_split[1:]),
                model_field.related_model,  # This fails only on bad input
            )

    field_type = convert_django_field_with_choices(model_field, required=False)

    # Handle this case by "deconstructing" the field type class, and pass it as an argument to
    # graphene.List
    if filter_field_is_list:
        field_type = graphene.List(type(field_type), required=False)

    return field_type
    def test__field_has_default__required_is_set_appropriately(self):
        class MockModel(models.Model):
            field_with_choices = models.CharField(max_length=16,
                                                  choices=(
                                                      ("A", "Choice a"),
                                                      ("B", "Choice b"),
                                                      ("C", "Choice c"),
                                                  ),
                                                  default="A")

        field = MockModel._meta.get_field("field_with_choices")
        result = convert_django_field_with_choices(field, )

        self.assertIsInstance(result, graphene.types.Enum)
        self.assertEqual(result.kwargs.get("required"), False)
Beispiel #5
0
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
Beispiel #6
0
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