Search code examples
djangotastypie

Tastypie obj_create bundle gives invalid literal for int() with base 10


I'm using Tastypie to POST and create a new Resource, which works fine:

   class TestResource(ModelResource):
    class Meta:
        queryset = MemberParticipant.objects.all()
        resource_name = 'participant'
        allowed_methods = ['post']

        def obj_create(self, bundle, request=None, **kwargs):
            """
            Creates a new object based on the provided data.
            This method overwrites the resource create
            as we need to preform extra method calls.
            """
            bundle = self.full_hydrate(bundle)
            # Check data is valid before trying to create a new resource.
            self.is_valid(bundle)
            if bundle.errors:
                raise ImmediateHttpResponse(response=self.error_response(bundle.request, bundle.errors))

            new_user = MemberParticipant.objects.create_user(email=bundle.data['email'],
                                                         password=bundle.data['password'])
            # Log the user in
            email = new_user.email
            password = bundle.data['password']
            user = authenticate(username=email, password=password)

            login(bundle.request, user)
            return bundle

I then use in the Meta class always_return_data = True to always return the created resource in the AJAX call.

However, doing this gives now gives me the following error:

invalid literal for int() with base 10: /django-env/lib/python2.7/site-packages/tastypie/fields.py\", line 234, in convert\n return int(value)\n\nValueError: invalid literal for int() with base 10: ''\n"}

Why?

Full error traceback:

{
    "error_message": "invalid literal for int() with base 10: ''",
    "traceback": "Traceback (most recent call last):
  File \"/Users/user/Documents/workspace/test/django-env/lib/python2.7/site-packages/tastypie/resources.py\", line 195, in wrapper
    response = callback(request, *args, **kwargs)
  File \"/Users/user/Documents/workspace/test/django-env/lib/python2.7/site-packages/tastypie/resources.py\", line 426, in dispatch_list
    return self.dispatch('list', request, **kwargs)
  File \"/Users/user/Documents/workspace/test/django-env/lib/python2.7/site-packages/tastypie/resources.py\", line 458, in dispatch
    response = method(request, **kwargs)
  File \"/Users/user/Documents/workspace/test/django-env/lib/python2.7/site-packages/tastypie/resources.py\", line 1326, in post_list
    updated_bundle = self.full_dehydrate(updated_bundle)
  File \"/Users/user/Documents/workspace/test/django-env/lib/python2.7/site-packages/tastypie/resources.py\", line 832, in full_dehydrate
    bundle.data[field_name] = field_object.dehydrate(bundle, for_list=for_list)
  File \"/Users/user/Documents/workspace/test/django-env/lib/python2.7/site-packages/tastypie/fields.py\", line 135, in dehydrate
    return self.convert(current_object)
  File \"/Users/user/Documents/workspace/test/django-env/lib/python2.7/site-packages/tastypie/fields.py\", line 234, in convert
    return int(value)

ValueError: invalid literal for int() with base 10: ''"
}

Solution

  • In short, you are missing bundle.obj and @mariodev is correct that if you just assign the object from create_user() to this bundle object it'll work. But take a step back and there is an easier way.

    The purpose of ModelResource.obj_create() is to create a new model object, that object is stored as bundle.obj, and Tastypie has all the code to perform this. The only task you are doing differently is to login a newly created user. Yet you override obj_create(), duplicate some Tastypie code and skip out on a bunch more. You check validation and check for errors, but ModelResource will also check authorization and save related models, which you aren't doing. Tastypie also builds up bundle.obj differently. Well, specifically you aren't building that object, but if you were it'd only contain email and password. What if the user submitted their name and other data? (Depends on your input code of course, but here you are creating a brittle implementation assumption.) Point is, you can have Tastypie do all the work and inject your log-in logic more simply.

    class TestResource(ModelResource):
        ...
    
        def obj_create(self, bundle, **kwargs):
            bundle = super(TestResource, self).obj_create(bundle, **kwargs)
            user = authenticate(username=bundle.obj.email, password=bundle.obj.password)
            if user is not None and user.is_active:
                login(bundle.request, user)
            return bundle
    

    Two more points: Perhaps at this point you could do username=bundle.obj.username if your model has this field. I took the user/active check from Django's docs here.