def remove_invalid_fields(self, queryset, fields, view, request):
        valid_fields = [
            item[0] for item in self.get_valid_fields(queryset, view,
                                                      {'request': request})
        ]
        bad_terms = [
            term for term in fields
            if format_value(term.replace(".", "__").lstrip('-'), "underscore")
            not in valid_fields
        ]
        if bad_terms:
            raise ValidationError('invalid sort parameter{}: {}'.format(
                ('s' if len(bad_terms) > 1 else ''), ','.join(bad_terms)))
        # this looks like it duplicates code above, but we want the ValidationError to report
        # the actual parameter supplied while we want the fields passed to the super() to
        # be correctly rewritten.
        # The leading `-` has to be stripped to prevent format_value from turning it into `_`.
        underscore_fields = []
        for item in fields:
            item_rewritten = item.replace(".", "__")
            if item_rewritten.startswith('-'):
                underscore_fields.append(
                    '-' +
                    format_value(item_rewritten.lstrip('-'), "underscore"))
            else:
                underscore_fields.append(
                    format_value(item_rewritten, "underscore"))

        return super(JSONAPIOrderingFilter,
                     self).remove_invalid_fields(queryset, underscore_fields,
                                                 view, request)
    def remove_invalid_fields(self, queryset, fields, view, request):
        """
        Extend :py:meth:`rest_framework.filters.OrderingFilter.remove_invalid_fields` to
        validate that all provided sort fields exist (as contrasted with the super's behavior
        which is to silently remove invalid fields).

        :raises ValidationError: if a sort field is invalid.
        """
        valid_fields = [
            item[0] for item in self.get_valid_fields(queryset, view,
                                                      {'request': request})
        ]
        bad_terms = [
            term for term in fields
            if format_value(term.replace(".", "__").lstrip('-'), "underscore") not in valid_fields
        ]
        if bad_terms:
            raise ValidationError('invalid sort parameter{}: {}'.format(
                ('s' if len(bad_terms) > 1 else ''), ','.join(bad_terms)))
        # this looks like it duplicates code above, but we want the ValidationError to report
        # the actual parameter supplied while we want the fields passed to the super() to
        # be correctly rewritten.
        # The leading `-` has to be stripped to prevent format_value from turning it into `_`.
        underscore_fields = []
        for item in fields:
            item_rewritten = item.replace(".", "__")
            if item_rewritten.startswith('-'):
                underscore_fields.append(
                    '-' + format_value(item_rewritten.lstrip('-'), "underscore"))
            else:
                underscore_fields.append(format_value(item_rewritten, "underscore"))

        return super(OrderingFilter, self).remove_invalid_fields(
            queryset, underscore_fields, view, request)
 def to_internal_value(self, data):
     matched_fields = set(self.alt_structures) & set(
         self.initial_data.keys())
     formatted_fields = [
         format_value(f) for f in sorted(self.alt_structures)
     ]
     formatted_matched_fields = [
         format_value(f) for f in sorted(matched_fields)
     ]
     if not matched_fields:
         raise ValidationError({
             "non_field_errors":
             f"One of {sorted(formatted_fields)} required."
         })
     if len(matched_fields) > 1:
         raise ValidationError({
             "non_field_errors":
             (f"Only one of {formatted_fields} allowed. "
              f"Recieved {formatted_matched_fields}.")
         })
     data = super().to_internal_value(data)  # calls field validators
     structure = next(k for k in self.alt_structures if k in data)
     data["molfile_v3000"] = get_molfile_v3000(data.pop(structure))
     if "inchikey" not in data:
         data["inchikey"] = get_inchikey(data["molfile_v3000"])
     return data
    def test_get_related_field_name_handles_formatted_link_segments(
        self, format_links, rf
    ):
        # use field name which actually gets formatted
        related_model_field_name = "related_field_model"

        class RelatedFieldNameSerializer(serializers.ModelSerializer):
            related_model_field = ResourceRelatedField(queryset=BasicModel.objects)

            def __init__(self, *args, **kwargs):
                self.related_model_field.field_name = related_model_field_name
                super().__init(*args, **kwargs)

            class Meta:
                model = BasicModel

        class RelatedFieldNameView(ModelViewSet):
            serializer_class = RelatedFieldNameSerializer

        url_segment = format_value(related_model_field_name, format_links)

        request = rf.get(f"/basic_models/1/{url_segment}")

        view = RelatedFieldNameView()
        view.setup(request, related_field=url_segment)

        assert view.get_related_field_name() == related_model_field_name
예제 #5
0
 def json(cls, *args, **kwargs):
     """Render a JSON POST request."""
     serializer = cls.build(*args, **kwargs)
     attributes = {}
     relationships = {}
     for key, value in serializer.initial_data.items():
         if is_relation(value):
             relationships[format_value(key)] = {"data": value}
         else:
             attributes[format_value(key)] = value
     data = {"type": get_resource_type_from_serializer(serializer)}
     if attributes:
         data["attributes"] = attributes
     if relationships:
         data["relationships"] = relationships
     return JSONRenderer().render({"data": data})
예제 #6
0
    def get_filterset_kwargs(self, request, queryset, view):
        """
        Turns filter[<field>]=<value> into <field>=<value> which is what
        DjangoFilterBackend expects

        :raises ValidationError: for bad filter syntax
        """
        filter_keys = []
        # rewrite filter[field] query params to make DjangoFilterBackend work.
        data = request.query_params.copy()
        for qp, val in data.items():
            m = self.filter_regex.match(qp)
            if m and (not m.groupdict()['assoc'] or
                      m.groupdict()['ldelim'] != '[' or m.groupdict()['rdelim'] != ']'):
                raise ValidationError("invalid query parameter: {}".format(qp))
            if m and qp != self.search_param:
                if not val:
                    raise ValidationError("missing {} test value".format(qp))
                # convert jsonapi relationship path to Django ORM's __ notation
                key = m.groupdict()['assoc'].replace('.', '__')
                # undo JSON_API_FORMAT_FIELD_NAMES conversion:
                key = format_value(key, 'underscore')
                data[key] = val
                filter_keys.append(key)
                del data[qp]
        return {
            'data': data,
            'queryset': queryset,
            'request': request,
            'filter_keys': filter_keys,
        }
def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)

    if not response:
        return response

    errors = []
    # handle generic errors. ValidationError('test') in a view for example
    if isinstance(response.data, list):
        for message in response.data:
            errors.append({
                'detail': message,
                'source': {
                    'pointer': '/data',
                },
                'status': encoding.force_text(response.status_code),
            })
    # handle all errors thrown from serializers
    else:
        for field, error in response.data.items():
            field = format_value(field)
            pointer = '/data/attributes/{}'.format(field)
            # see if they passed a dictionary to ValidationError manually
            if isinstance(error, dict):
                errors.append(error)
            elif isinstance(error, six.string_types):
                classes = inspect.getmembers(exceptions, inspect.isclass)
                # DRF sets the `field` to 'detail' for its own exceptions
                if isinstance(exc, tuple(x[1] for x in classes)):
                    pointer = '/data'
                errors.append({
                    'detail': error,
                    'source': {
                        'pointer': pointer,
                    },
                    'status': encoding.force_text(response.status_code),
                })
            elif isinstance(error, list):
                for message in error:
                    errors.append({
                        'detail': message,
                        'source': {
                            'pointer': pointer,
                        },
                        'status': encoding.force_text(response.status_code),
                    })
            else:
                errors.append({
                    'detail': error,
                    'source': {
                        'pointer': pointer,
                    },
                    'status': encoding.force_text(response.status_code),
                })


    context['view'].resource_name = 'errors'
    response.data = errors
    return response
예제 #8
0
def test_get_related_field_name_handles_formatted_link_segments(
        format_links, rf):
    url_segment = format_value(related_model_field_name, format_links)

    request = rf.get(f"/basic_models/1/{url_segment}")

    view = BasicModelFakeViewSet()
    view.setup(request, related_field=url_segment)

    assert view.get_related_field_name() == related_model_field_name
예제 #9
0
    def remove_invalid_fields(self, queryset, fields, view, request):
        """
        Extend :py:meth:`rest_framework.filters.OrderingFilter.remove_invalid_fields` to
        validate that all provided sort fields exist (as contrasted with the super's behavior
        which is to silently remove invalid fields).

        :raises ValidationError: if a sort field is invalid.
        """
        valid_fields = [
            item[0]
            for item in self.get_valid_fields(queryset, view, {"request": request})
        ]
        bad_terms = [
            term
            for term in fields
            if format_value(term.replace(".", "__").lstrip("-"), "underscore")
            not in valid_fields
        ]
        if bad_terms:
            raise ValidationError(
                "invalid sort parameter{}: {}".format(
                    ("s" if len(bad_terms) > 1 else ""), ",".join(bad_terms)
                )
            )
        # this looks like it duplicates code above, but we want the ValidationError to report
        # the actual parameter supplied while we want the fields passed to the super() to
        # be correctly rewritten.
        # The leading `-` has to be stripped to prevent format_value from turning it into `_`.
        underscore_fields = []
        for item in fields:
            item_rewritten = item.replace(".", "__")
            if item_rewritten.startswith("-"):
                underscore_fields.append(
                    "-" + format_value(item_rewritten.lstrip("-"), "underscore")
                )
            else:
                underscore_fields.append(format_value(item_rewritten, "underscore"))

        return super(OrderingFilter, self).remove_invalid_fields(
            queryset, underscore_fields, view, request
        )
예제 #10
0
    def test_parse_formats_field_names(
        self,
        settings,
        format_field_names,
        parse,
    ):
        settings.JSON_API_FORMAT_FIELD_NAMES = format_field_names

        data = {
            "data": {
                "id": "123",
                "type": "BasicModel",
                "attributes": {
                    format_value("test_attribute", format_field_names):
                    "test-value"
                },
                "relationships": {
                    format_value("test_relationship", format_field_names): {
                        "data": {
                            "type": "TestRelationship",
                            "id": "123"
                        }
                    }
                },
            }
        }

        result = parse(data)
        assert result == {
            "id": "123",
            "test_attribute": "test-value",
            "test_relationship": {
                "id": "123",
                "type": "TestRelationship"
            },
        }
예제 #11
0
    def get_serializer_info(self, serializer):
        """
        Given an instance of a serializer, return a dictionary of metadata
        about its fields.
        """
        if hasattr(serializer, 'child'):
            # If this is a `ListSerializer` then we want to examine the
            # underlying child serializer instance instead.
            serializer = serializer.child

        # Remove the URL field if present
        serializer.fields.pop(api_settings.URL_FIELD_NAME, None)

        return OrderedDict([
            (format_value(field_name), self.get_field_info(field))
            for field_name, field in serializer.fields.items()
        ])
    def get_filterset_kwargs(self, request, queryset, view):
        """
        Turns filter[<field>]=<value> into <field>=<value> which is what
        DjangoFilterBackend expects

        :raises ValidationError: for bad filter syntax
        """
        filter_keys = []
        # rewrite filter[field] query params to make DjangoFilterBackend work.
        data = request.query_params.copy()
        for qp, val in request.query_params.lists():
            m = self.filter_regex.match(qp)
            if m and (
                not m.groupdict()["assoc"]
                or m.groupdict()["ldelim"] != "["
                or m.groupdict()["rdelim"] != "]"
            ):
                raise ValidationError("invalid query parameter: {}".format(qp))
            if m and qp != self.search_param:
                if not all(val):
                    raise ValidationError(
                        "missing value for query parameter {}".format(qp)
                    )
                # convert jsonapi relationship path to Django ORM's __ notation
                key = m.groupdict()["assoc"].replace(".", "__")
                # undo JSON_API_FORMAT_FIELD_NAMES conversion:
                key = format_value(key, "underscore")
                data.setlist(key, val)
                filter_keys.append(key)
                del data[qp]
        return {
            "data": data,
            "queryset": queryset,
            "request": request,
            "filter_keys": filter_keys,
        }
예제 #13
0
def test_format_value_deprecates_default_format_type_argument():
    with pytest.deprecated_call():
        assert "first_name" == format_value("first_name")
예제 #14
0
def test_format_value(settings, format_type, output):
    assert format_value("first_name", format_type) == output
예제 #15
0
 def _format_key(self, s):
     return format_value(s)
예제 #16
0
 def dispatch(self, request, *args, **kwargs):
     if "related_field" in kwargs:
         kwargs["related_field"] = format_value(
             self.kwargs["related_field"], "underscore")
     return super().dispatch(request, *args, **kwargs)
예제 #17
0
 def format_string(self, s):
     return format_value(s)
 def get_url(self, name, view_name, kwargs, request):
     if "related_field" in kwargs:
         kwargs["related_field"] = format_value(kwargs["related_field"])
     return super().get_url(name, view_name, kwargs, request)
def exception_handler(exc, context):
    # Import this here to avoid potential edge-case circular imports, which
    # crashes with:
    # "ImportError: Could not import 'rest_framework_json_api.parsers.JSONParser' for API setting
    # 'DEFAULT_PARSER_CLASSES'. ImportError: cannot import name 'exceptions'.'"
    #
    # Also see: https://github.com/django-json-api/django-rest-framework-json-api/issues/158
    from rest_framework.views import exception_handler as drf_exception_handler
    response = drf_exception_handler(exc, context)

    if not response:
        return response

    errors = []
    # handle generic errors. ValidationError('test') in a view for example
    if isinstance(response.data, list):
        for message in response.data:
            errors.append({
                'detail': message,
                'source': {
                    'pointer': '/data',
                },
                'status': encoding.force_text(response.status_code),
            })
    # handle all errors thrown from serializers
    else:
        for field, error in response.data.items():
            field = format_value(field)
            pointer = '/data/attributes/{}'.format(field)
            # see if they passed a dictionary to ValidationError manually
            if isinstance(error, dict):
                errors.append(error)
            elif isinstance(error, six.string_types):
                classes = inspect.getmembers(exceptions, inspect.isclass)
                # DRF sets the `field` to 'detail' for its own exceptions
                if isinstance(exc, tuple(x[1] for x in classes)):
                    pointer = '/data'
                errors.append({
                    'detail': error,
                    'source': {
                        'pointer': pointer,
                    },
                    'status': encoding.force_text(response.status_code),
                })
            elif isinstance(error, list):
                for message in error:
                    errors.append({
                        'detail': message,
                        'source': {
                            'pointer': pointer,
                        },
                        'status': encoding.force_text(response.status_code),
                    })
            else:
                errors.append({
                    'detail': error,
                    'source': {
                        'pointer': pointer,
                    },
                    'status': encoding.force_text(response.status_code),
                })


    context['view'].resource_name = 'errors'
    response.data = errors
    return response
예제 #20
0
 def get_related_field_name(self):
     field_name = self.kwargs["related_field"]
     return format_value(field_name, "underscore")
def exception_handler(exc, context):
    # Import this here to avoid potential edge-case circular imports, which
    # crashes with:
    # "ImportError: Could not import 'rest_framework_json_api.parsers.JSONParser' for API setting
    # 'DEFAULT_PARSER_CLASSES'. ImportError: cannot import name 'exceptions'.'"
    #
    # Also see: https://github.com/django-json-api/django-rest-framework-json-api/issues/158
    from rest_framework.views import exception_handler as drf_exception_handler
    response = drf_exception_handler(exc, context)

    if not response:
        return response

    errors = []
    # handle generic errors. ValidationError('test') in a view for example
    if isinstance(response.data, list):
        for message in response.data:
            errors.append({
                'detail': message,
                'source': {
                    'pointer': '/data',
                },
                'status': encoding.force_text(response.status_code),
            })
    # handle all errors thrown from serializers
    else:
        for field, error in response.data.items():
            field = format_value(field)
            pointer = '/data/attributes/{}'.format(field)
            # see if they passed a dictionary to ValidationError manually
            if isinstance(error, dict):
                errors.append(error)
            elif isinstance(error, six.string_types):
                classes = inspect.getmembers(exceptions, inspect.isclass)
                # DRF sets the `field` to 'detail' for its own exceptions
                if isinstance(exc, tuple(x[1] for x in classes)):
                    pointer = '/data'
                errors.append({
                    'detail':
                    error,
                    'source': {
                        'pointer': pointer,
                    },
                    'status':
                    encoding.force_text(response.status_code),
                })
            elif isinstance(error, list):
                for message in error:
                    errors.append({
                        'detail':
                        message,
                        'source': {
                            'pointer': pointer,
                        },
                        'status':
                        encoding.force_text(response.status_code),
                    })
            else:
                errors.append({
                    'detail':
                    error,
                    'source': {
                        'pointer': pointer,
                    },
                    'status':
                    encoding.force_text(response.status_code),
                })

    context['view'].resource_name = 'errors'
    response.data = errors
    return response
def test_format_value():
    assert utils.format_value('first_name', 'camelize') == 'firstName'
    assert utils.format_value('first_name', 'capitalize') == 'FirstName'
    assert utils.format_value('first_name', 'dasherize') == 'first-name'
    assert utils.format_value('first-name', 'underscore') == 'first_name'
예제 #23
0
def test_format_value():
    assert utils.format_value('first_name', 'camelize') == 'firstName'
    assert utils.format_value('first_name', 'capitalize') == 'FirstName'
    assert utils.format_value('first_name', 'dasherize') == 'first-name'
    assert utils.format_value('first-name', 'underscore') == 'first_name'
def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)

    if not response:
        return response

    errors = []
    # handle generic errors. ValidationError('test') in a view for example
    if isinstance(response.data, list):
        for message in response.data:
            errors.append({
                'detail': message,
                'source': {
                    'pointer': '/data',
                },
                'status': encoding.force_text(response.status_code),
            })
    # handle all errors thrown from serializers
    else:
        for field, error in response.data.items():
            field = format_value(field)
            pointer = '/data/attributes/{}'.format(field)
            # see if they passed a dictionary to ValidationError manually
            if isinstance(error, dict):
                errors.append(error)
            elif isinstance(error, six.string_types):
                classes = inspect.getmembers(exceptions, inspect.isclass)
                # DRF sets the `field` to 'detail' for its own exceptions
                if isinstance(exc, tuple(x[1] for x in classes)):
                    pointer = '/data'
                errors.append({
                    'detail':
                    error,
                    'source': {
                        'pointer': pointer,
                    },
                    'status':
                    encoding.force_text(response.status_code),
                })
            elif isinstance(error, list):
                for message in error:
                    errors.append({
                        'detail':
                        message,
                        'source': {
                            'pointer': pointer,
                        },
                        'status':
                        encoding.force_text(response.status_code),
                    })
            else:
                errors.append({
                    'detail':
                    error,
                    'source': {
                        'pointer': pointer,
                    },
                    'status':
                    encoding.force_text(response.status_code),
                })

    context['view'].resource_name = 'errors'
    response.data = errors
    return response