Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP


Avoiding PUT/POST to rebound data #615

wants to merge 1 commit into from

5 participants


full_dehydrate assumes 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 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.



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


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


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



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


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


I just got bitten by this exact bug (see 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
Commits on Aug 14, 2012
  1. @buzzeante
This page is out of date. Refresh to see the latest.
2  tastypie/
@@ -709,6 +709,8 @@ def full_dehydrate(self, bundle):
Given a bundle with an object instance, extract the information from it
to populate the resource.
+ # clear data that will be returned
+ = {}
# Dehydrate each field.
for field_name, field_object in self.fields.items():
# A touch leaky but it makes URI resolution work.
8 tests/basic/api/
@@ -36,6 +36,14 @@ class Meta:
authorization = Authorization()
+class FullNoteResource(ModelResource):
+ class Meta:
+ resource_name = 'full_note'
+ queryset = Note.objects.all()
+ authorization = Authorization()
+ always_return_data = True
class BustedResource(ModelResource):
class Meta:
queryset = AnnotatedNote.objects.all()
3  tests/basic/api/
@@ -1,6 +1,6 @@
from django.conf.urls.defaults import *
from tastypie.api import Api
-from basic.api.resources import NoteResource, UserResource, BustedResource, CachedUserResource, SlugBasedNoteResource, SessionUserResource
+from basic.api.resources import NoteResource, UserResource, BustedResource, CachedUserResource, SlugBasedNoteResource, SessionUserResource, FullNoteResource
api = Api(api_name='v1')
api.register(NoteResource(), canonical=True)
@@ -9,6 +9,7 @@
v2_api = Api(api_name='v2')
v2_api.register(BustedResource(), canonical=True)
14 tests/basic/tests/
@@ -61,6 +61,20 @@ def test_post_object(self):
self.assertEqual(obj['is_active'], True)
self.assertEqual(obj['user'], '/api/v1/users/1/')
+ def test_put_object(self):
+ connection = self.get_connection()
+ 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"}'
+ connection.request('PUT', '/api/v2/full_note/2/', body=post_data, headers={'Accept': 'application/json', 'Content-type': 'application/json'})
+ response = connection.getresponse()
+ self.assertEqual(response.status, 202)
+ # make sure no data is rebounded
+ data =
+ obj = json.loads(data)
+ self.assertNotIn('foo-bar', obj)
+ self.assertEqual(obj['content'], "A new post modified.")
def test_cache_control(self):
"""Ensure that resources can specify custom cache control directives"""
connection = self.get_connection()
Something went wrong with that request. Please try again.