[Answered ]-Django Tastypie Many to Many (self) field update using a PATCH or PUT request?

2👍

I can’t be sure it’s the same as your situation, but I found my problem.

Let me modify your example slightly to reflect the situation I encountered:

class UserResource(ModelResource):
    usersubs = fields.ToManyField('test.api.UserSubResource', attribute = 'usersubs', full=True, null=True)
    specialUsersub = fields.ToOneField('test.api.UserSubResource', attribute = 'special_user_sub', full=True, null=True) 
    class Meta:
        object_class = User
        fields = ['username', 'first_name', 'last_name', 'password', 'email']
        detail_allowed_methods = ['get', 'post', 'put']
        authentication = Authentication()
        authorization = Authorization()
        queryset = User.objects.all()
        resource_name = 'users'
        excludes = ['id']

class UserSubResource(ModelResource):
    amigos= fields.ToManyField('test.api.UserSubResource', attribute = 'amigos', null=True)      
    class Meta:
        object_class = UserSub
        fields = ['title']
        detail_allowed_methods = ['get', 'post', 'put', 'patch']
        authentication = Authentication()
        authorization = Authorization()
        always_return_data = True
        queryset = UserSub.objects.all()
        resource_name = 'usersub'
        excludes = ['id']

and the request:

PATCH /users/1/
{ "specialusersub" : { "id" : 3, "amigos" : ["/api/v1/usersub/9/","/api/v1/usersub/8/"] } }

In my case this problem was caused by trying to patch a ToMany resource nested two levels deep when the parent resource also existed in a ToMany relationship at the top level. Because of the nesting and because of the order of fields on the resource, the order of operations happened like this:

  1. Hydrate usersubs into bundle (and its nested relationships – loaded from DB, in our case empty)
  2. Hydrate specialUsersub into bundle (and its nested relationships – included in request data)
  3. Save specialUsersub (nested resources correctly saved here)
  4. [At this point tastypie should check if any of its hydrated resources were saved and rehydrate the appropriate pieces of the bundle but it doesn’t, so:]
  5. Save usersubs (if the resource at specialUsersub also exists in usersubs then what was previously saved will be overwritten with the stale data that was loaded when usersubs was hydrated in step 1)

To be more precise, because tastypie deliberately clears all m2m relationships before repopulating them with what’s stored in the bundle, the stale data clobbers the new data, the amigos created in 3 are deleted and replaced with the [] empty list that was loaded in 1.

I’m still testing, but I think the solution/hack is to ensure that you include the updated nested resource whereever your tastypie resource might be expecting it:

PATCH /users/1/
{ "usersubs" : [{ "id" : 3, "amigos" : ["/api/v1/usersub/9/","/api/v1/usersub/8/"] }], "specialusersub" : { "id" : 3, "amigos" : ["/api/v1/usersub/9/","/api/v1/usersub/8/"] } }

Obviously that’s not ideal. I’ll let you know if I come up with a more appropriate solution.

👤kball

Leave a comment