Skip to content
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

DRF3 support #29

Merged
merged 16 commits into from
Feb 9, 2015
Merged

DRF3 support #29

merged 16 commits into from
Feb 9, 2015

Conversation

miki725
Copy link
Owner

@miki725 miki725 commented Feb 2, 2015

Fixes #23

Some ideas taken from #24.

The major difference in the approach here vs one in #24 is that instead of only supporting DRF3, this PR supports both 2 and 3. This is especially important since many of existing APIs have not migrated to DRF3 yet (including some of my own so sorry for being a little selfish here). How we accomplish that is by having separate packages for DRF2 and DRF3. This way we dont have to do crazy things to support both. I understand this adds code duplication however in a long run I think this is nicer and cleaner approach. In addition this is allows to only new methods introduced in DRF3 without mixing them with older DRF2 methods such as pre_save, etc.

Eventually when this library will only support DRF3, we can then simply move the DRF3 mixins from its own package to the root of the library and no other changes are going to be required.

TODO:

else:
serializer = self.get_serializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
Copy link
Owner Author

Choose a reason for hiding this comment

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

@kevin-brown from #24

perform_create is provided by the generics API, and it's most likely the primary override point for users who previously used the old save hooks. I'd recommend creating a new hook, perform_bulk_create and embedding logic in there to call perform_create if it exists, or fall back to the old pre_save and post_save loops if it doesn't.

Did you had any ideas how to do that since perform_create simply calls serializer.save()?
Since serializer is then responsible for generating multiple objects, I am not sure what we can loop over here.

Copy link
Contributor

Choose a reason for hiding this comment

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

And ListSerializer.create just calls OriginalSerializer.create on each of the objects.

I'd just create a perform_bulk_create hook (that just calls serializer.save) and leave it at that.

The other option is to split away from ListSerializer.create and handle creation on your own by re-creating the loop that it usually uses and calling your own hooks instead.

# this allows to maintain clean code for each DRF version
# without doing any magic
# a little more code but a lit clearer what is going on
if str(rest_framework.__version__).startswith('2'):
Copy link
Owner Author

Choose a reason for hiding this comment

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

how so? we might have to wait a while to get to DRF 20+

Copy link
Owner Author

Choose a reason for hiding this comment

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

in case that is changed to some other datastructure but not necessary now

@miki725
Copy link
Owner Author

miki725 commented Feb 8, 2015

Just pushed latest touches and wondering if someone is willing to test this branch in their projects before I merge.

ccing everyone from #23 and #24

@kevin-brown @merll @gabriel-amram @bogdanpetrea @anyong

# does not actually do anything with the returned object
for file, line, function, code in traceback.extract_stack():
if all((file.endswith('rest_framework/views.py'),
function == 'options')):
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe it would be better here just to check self.request.method?

Copy link
Owner Author

Choose a reason for hiding this comment

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

I was thinking if people use a custom router, they might be using metadata() or options() some other place (like offline doc generation). Is that a possibility?

I agree this is not the cleanest way of doing it. I would of used self.action buts thats only available in viewsets but this is necessary in views only when metadata is generated.

Copy link
Owner Author

Choose a reason for hiding this comment

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

@tomchristie maybe you can advise?

Choose a reason for hiding this comment

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

I'm sorry I don't understand why AssertionError is being caught there so from a brief look I've no idea. Happy to look in more detail during the week.

Copy link
Owner Author

Choose a reason for hiding this comment

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

@tomchristie here is the context:

In the super implementation of the get_object(), it asserts that lookup_url_kwarg is present within self.kwargs. That makes sense since get_object() is usually called in a URL for a single resource (e.g. /api/resource/<pk>/) so lookup_url_kwarg will always be present.

The challenge here is that we are trying to add bulk operations with support for PUT requests to a list resource which obviously will be lacking a lookup_url_kwarg. More particularly the exception occurs when making OPTIONS request to the api which uses metadata class to get api docs which internally calls get_object.

Here is what is my end goal - detect when metadata is generated in which case ignore the assertion error but in all other cases rerase the exception since that most likely indicates that something is indeed wrong and exception should not be ignored.

What would be the best way of achieving that?

@kevin-brown suggested that we can simply check if self.request.method == 'OPTIONS' and then reraise exception but I am not sure if that will catch all the use-cases.

Choose a reason for hiding this comment

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

What would be the best way of achieving that?

I'd probably suggest fully overriding get_object so that it actually has the correct behavior that you need for bulk updates.

miki725 added a commit that referenced this pull request Feb 9, 2015
@miki725 miki725 merged commit eeead95 into master Feb 9, 2015
@miki725 miki725 deleted the drf3 branch February 9, 2015 11:01
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.

Support for DRF 3.0
3 participants