Skip to content
This repository

Avoiding PUT/POST to rebound data #615

Open
wants to merge 1 commit into from

5 participants

Javier Chamizo Don't Add Me To Your Organization a.k.a The Travis Bot Josh Bohde Ondrej Slinták Roy Smith
Javier Chamizo

full_dehydrate assumes bundle.data to be empty so it just populates elements from bundle.obj. This works fine with GET. However, when POST or PUT request are done the same bundle goes through all the flow. As a result, data that came from the request and is not part of the resource is rebounded in the response.

The fix avoids this problem by reseting bundle.data to an empty dictionary before starting the dehydration process.

When using Tastypie with BackboneJS, this data rebounded tampers models causing side effects very hard to detect.

Cheers,
Javi

Don't Add Me To Your Organization a.k.a The Travis Bot

This pull request fails (merged 438d642 into 9479190).

Josh Bohde
Collaborator

Thanks for the pull request. Before this could get merged, we'd need to have a failing test, per our contribution guidelines.

Javier Chamizo

Thanks Josh. I've just added a test for it.

Cheers,
Javi

Don't Add Me To Your Organization a.k.a The Travis Bot

This pull request passes (merged df29682 into 5397dec).

Ondrej Slinták

What's the status on this? I like this solution. My only concern is if there aren't cases when contents of bundle.data might be useful. Couldn't come up with any use cases though.

Roy Smith

I just got bitten by this exact bug (see https://groups.google.com/d/topic/django-tastypie/WE_d92Fkl-I/discussion). It would be great if this patch could be included in the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Aug 14, 2012
Javier Chamizo buzzeante Avoiding PUT/POST to rebound data df29682
This page is out of date. Refresh to see the latest.
2  tastypie/resources.py
@@ -709,6 +709,8 @@ def full_dehydrate(self, bundle):
709 709 Given a bundle with an object instance, extract the information from it
710 710 to populate the resource.
711 711 """
  712 + # clear data that will be returned
  713 + bundle.data = {}
712 714 # Dehydrate each field.
713 715 for field_name, field_object in self.fields.items():
714 716 # A touch leaky but it makes URI resolution work.
8 tests/basic/api/resources.py
@@ -36,6 +36,14 @@ class Meta:
36 36 authorization = Authorization()
37 37
38 38
  39 +class FullNoteResource(ModelResource):
  40 + class Meta:
  41 + resource_name = 'full_note'
  42 + queryset = Note.objects.all()
  43 + authorization = Authorization()
  44 + always_return_data = True
  45 +
  46 +
39 47 class BustedResource(ModelResource):
40 48 class Meta:
41 49 queryset = AnnotatedNote.objects.all()
3  tests/basic/api/urls.py
... ... @@ -1,6 +1,6 @@
1 1 from django.conf.urls.defaults import *
2 2 from tastypie.api import Api
3   -from basic.api.resources import NoteResource, UserResource, BustedResource, CachedUserResource, SlugBasedNoteResource, SessionUserResource
  3 +from basic.api.resources import NoteResource, UserResource, BustedResource, CachedUserResource, SlugBasedNoteResource, SessionUserResource, FullNoteResource
4 4
5 5 api = Api(api_name='v1')
6 6 api.register(NoteResource(), canonical=True)
@@ -9,6 +9,7 @@
9 9
10 10 v2_api = Api(api_name='v2')
11 11 v2_api.register(BustedResource(), canonical=True)
  12 +v2_api.register(FullNoteResource())
12 13 v2_api.register(SlugBasedNoteResource())
13 14 v2_api.register(SessionUserResource())
14 15
14 tests/basic/tests/http.py
@@ -61,6 +61,20 @@ def test_post_object(self):
61 61 self.assertEqual(obj['is_active'], True)
62 62 self.assertEqual(obj['user'], '/api/v1/users/1/')
63 63
  64 + def test_put_object(self):
  65 + connection = self.get_connection()
  66 + post_data = '{"content": "A new post modified.", "is_active": true, "title": "New Title", "slug": "new-title", "user": "/api/v1/users/1/", "foo-bar": "this should not be in the response"}'
  67 + connection.request('PUT', '/api/v2/full_note/2/', body=post_data, headers={'Accept': 'application/json', 'Content-type': 'application/json'})
  68 + response = connection.getresponse()
  69 + self.assertEqual(response.status, 202)
  70 +
  71 + # make sure no data is rebounded
  72 + data = response.read()
  73 + obj = json.loads(data)
  74 +
  75 + self.assertNotIn('foo-bar', obj)
  76 + self.assertEqual(obj['content'], "A new post modified.")
  77 +
64 78 def test_cache_control(self):
65 79 """Ensure that resources can specify custom cache control directives"""
66 80 connection = self.get_connection()

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.