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

旧时模样 提交于 2019-12-13 17:28:16

问题


I have this model:

class UserSub(models.Model):
    user = models.OneToOneField(User, related_name='userSub')
    amigos = models.ManyToManyField('self', null=True)
    title = models.TextField()

Imported Django User Model.

And the following resources:

class UserResource(ModelResource):
    usersub = fields.OneToOneField('test.api.UserSubResource', attribute = 'personal', related_name='user', 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):
    user = fields.OneToOneField('test.api.UserResource', attribute = 'user', related_name = 'userSub')
    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'] 

I am trying to update amigos values for a specific user. My data is:

usersub_json: {"amigos":["/api/v1/usersub/9/","/api/v1/usersub/8/"]}

$.ajax({
        url : 'http://127.0.0.1:8000' + usersub_uri,
                        type : 'PUT',
                        contentType : 'application/json',
                        data : usersub_json,
                        dataType : 'json',
                        processData : false,
                        error : function(http) {
                            if (http.responseText != "") {
                                alert(http.responseText);
                            }
                        }
                    })

I am getting "202 ACCEPTED" from PUT request, and the amigos are not updated.

And "202 ACCEPTED" from PATCH request, and the amigos are not updated.

If I add the amigos in the first post request when creating a usersub, it adds them to the database successfully. But doesn't update if I add more to the array using PUT or PATCH.


回答1:


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.



来源:https://stackoverflow.com/questions/15987186/django-tastypie-many-to-many-self-field-update-using-a-patch-or-put-request

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!