Skip to content

support nested structures #776

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
May 14, 2020
Merged

Conversation

bpleshakov
Copy link
Contributor

@bpleshakov bpleshakov commented Mar 25, 2020

Fixes #769

Description of the Change

Checklist

  • PR only contains one change (considered splitting up PR)
  • unit-test added
  • documentation updated
  • CHANGELOG.md updated (only for user relevant changes)
  • author name in AUTHORS

@bpleshakov
Copy link
Contributor Author

bpleshakov commented Mar 25, 2020

@sliverc Hi. I made PR we discussed here #769
Please link that issue with the pull request.

UPD:
Coverage check shows that there is quite a lot of uncovered code that I didn't change and that is not quite related to this pr. What should we do with this issue?

UPD 2:
I haven't found doc section that describes settings available in JSONAPISettings. Where I am supposed to update doc?

@codecov
Copy link

codecov bot commented Mar 25, 2020

Codecov Report

Merging #776 into master will increase coverage by 0.03%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #776      +/-   ##
==========================================
+ Coverage   97.32%   97.36%   +0.03%     
==========================================
  Files          56       56              
  Lines        2920     2962      +42     
==========================================
+ Hits         2842     2884      +42     
  Misses         78       78              
Impacted Files Coverage Δ
rest_framework_json_api/settings.py 100.00% <ø> (ø)
example/tests/unit/test_renderers.py 100.00% <100.00%> (ø)
rest_framework_json_api/renderers.py 87.46% <100.00%> (+0.40%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1318610...deec195. Read the comment docs.

Copy link
Contributor

@auvipy auvipy left a comment

Choose a reason for hiding this comment

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

what about increasing the coverage? & are you sure this is compliant with 1.0 or 1.1 specs of json-api?

@bpleshakov
Copy link
Contributor Author

bpleshakov commented Mar 26, 2020

@auvipy yes it is complaint with 1.0 json-api spec.
Please, read #769

UPD:
I have done everything I could to increase coverage of this pr.
Any other uncovered code is strictly unrelated to this pr and has to be covered in other prs.

Copy link
Contributor

@auvipy auvipy left a comment

Choose a reason for hiding this comment

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

can you check the build failures?

@bpleshakov
Copy link
Contributor Author

@auvipy Yeah, I have checked. I really messed up.

Btw, don't merge this pr, I found out that there is an issue with error object generation, which I will resolve on Thursday.

@bpleshakov bpleshakov changed the title support nested structures WIP: support nested structures Mar 31, 2020
Copy link
Member

@sliverc sliverc left a comment

Choose a reason for hiding this comment

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

Thanks for working on this. See my inline comments. For error object object generation when looking through the code it is not totally obvious what causes this but hope you can tackle that issue as well.

Boris Pleshakov added 5 commits April 10, 2020 19:08
f json_api settings
f pytest.ini
…hat DeprecationWarning raises whenever nested serializer is used without correct settings
@bpleshakov bpleshakov changed the title WIP: support nested structures support nested structures Apr 14, 2020
@bpleshakov bpleshakov requested a review from sliverc April 14, 2020 11:00
Copy link
Member

@sliverc sliverc left a comment

Choose a reason for hiding this comment

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

Thanks for your updates. The PR is shaping up nicely - good work so far! There are some comments I have added though.

# 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
# The bit tricky problem is here. It is may be nested drf thing in format
# name: error_object, or it may be custom error thrown by user. I guess,
# if it is drf error, dict will always have single key
Copy link
Member

Choose a reason for hiding this comment

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

The comment is not quite clear what is happening here. Can you elaborate on this issue?

Copy link
Contributor Author

@bpleshakov bpleshakov Apr 19, 2020

Choose a reason for hiding this comment

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

For the record, all of this needed to put the correct pointer into an error object.

Well, the issue here is that the error object may be any valid dict with any string as a key and any object as a value.

Let consider two examples. First one is validator method in identity serializer
As you can see users can specify any dict whatever they want as a detailed message, and old behavior would be "pass it as is, do nothing". By the way, I am not entirely sure that old behavior is correct and we still should pass this object as is. At least we have to append pointer here if it is possible.

The second example is second level attribute exception
In this case, an error object will be

{
    "body": ["This field is required"]
}

where the body is a field name. Moreover, if we consider a more deep nested structure, the error object will be more complicated with nested dicts and lists. This is just how DRF validation works and passes errors.
But I am quite sure if we get a single dict DRF error it will be always a dict with a single key consists of field name and either error message, dict or list depending on the serializer field type.

Since we don't know from where the error object come out neither we don't know serializer structure at this point it is a tricky question how we can separate custom error object defined by users from that errors generated by DRF itself. I assumed this heuristic with a length of the dict is the simplest solution, even though it doesn't feel entirely correct.

The other possible solutions are:

  1. Overwrite serializer's validation process and extend the validation error class so we can put the correct pointer at the moment we get an error. I assume this is the most correct way to deal with these problems. However, it will take too much effort and I am not able to do that in the nearest future.

  2. The spec states:

An error object MAY have the following members:

id: a unique identifier for this particular occurrence of the problem.
links: a links object containing the following members:
about: a link that leads to further details about this particular occurrence of the problem.
status: the HTTP status code applicable to this problem, expressed as a string value.
code: an application-specific error code, expressed as a string value.
title: a short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization.
detail: a human-readable explanation specific to this occurrence of the problem. Like title, this field’s value can be localized.
source: an object containing references to the source of the error, optionally including any of the following members:
pointer: a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. "/data" for a primary data object, or "/data/attributes/title" for a specific attribute].
parameter: a string indicating which URI query parameter caused the error.
meta: a meta object containing non-standard meta-information about the error.

We can come up with another heuristic to check that if dict contains any subset of these fields. However it doesn't protect us in two problems: first, these fields may overlap with serializer fields and second, users may pass as a dict key whatever they want which may be neither serializer field or spec stated field.

I am not sure what we should do with this comment, I can expand it with detailed explanation copy-pasted from here with todo inserted or leave it as is with the issue reported.
I would prefer to report this to the doc as a known issue and merge this pr as is.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After a night with an idea of overwriting the validation process, I don't think it entirely solves this problem. We would still get all of this problems in case a user uses plain drf serializers

Copy link
Member

Choose a reason for hiding this comment

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

From a JSON API specification perspective the nested json is actually just a value and it suggests that an error is reported on an JSON API attribute level.
As we are having some issues concerning this would it not be better that we simply report an error on the attribute level and not on the nested value? I have a feeling this would also follow the spec closer although it doesn't seem to forbid to do more nested errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sliverc
I am not sure.
In my opinion, JSON:API spec is quite strict about what exactly pointer attribute must contain

pointer: a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. "/data" for a primary data object, or "/data/attributes/title" for a specific attribute].

So if we have an error somewhere deep in a nested structure, but we report it as it would happen in top-level attribute, it just doesn't feel correct and seems to be out of spec.
Moreover, it is not clear what exactly we have to put into the detail field. We aren't seem permitted to put here JSON object, detail has to be string, as far as I understand. If we will try to extract this string from the depth of the error object, we would fail into the same problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

By the way, I want to know should we inject the source object into the custom error object or not? I think It would be nice if we persist one if the error object doesn't have it already.

Copy link
Member

Choose a reason for hiding this comment

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

great that we have access to the serializer. In case of injecting of the source object. Yes that would be good, but only if it doesn't already exist. It is kind of blowing up this PR though. Our main issue at this point is error handling and the rest would be mergeable in this PR. So what do you think if we move the part of error handling into a second PR and merge this PR? In the second PR injecting source could also be part.

We won't release before this second PR would have been landed.

Copy link
Contributor

Choose a reason for hiding this comment

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

that sounds reasonable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree with that.

Copy link
Member

Choose a reason for hiding this comment

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

@sapiosexual Could you extract the error handling part from this PR into a new then? Thanks.

@bpleshakov bpleshakov mentioned this pull request May 13, 2020
5 tasks
Copy link
Member

@sliverc sliverc left a comment

Choose a reason for hiding this comment

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

Great. Thanks.

Copy link
Contributor

@auvipy auvipy left a comment

Choose a reason for hiding this comment

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

great job

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

DRF J-A renders nested serializers out of spec.
3 participants