Search code examples
pythondjangotastypie

Create a User Object with a Call to TastyPie API


I have a Tastypie resource for a model extended from django.contribut.auth's User model (just a few extra fields). Here's the resource code:

class CustomerResource(ModelResource):

    locations = fields.ToManyField('device.resources.LocationResource',
            'location_set', null=True)
    current_location = fields.ToOneField('device.resources.LocationResource',
            'current_location', null=True)
    default_location = fields.ToOneField('device.resources.LocationResource',
            'default_location', null=True)

    class Meta:
        queryset = Customer.objects.all()
        resource_name = 'customers'
        validation = CleanedDataFormValidation(form_class=RegistrationForm)
        list_allowed_methods = ['get', 'post']
        detail_allowed_methods = ['get', 'put', 'patch', 'delete']
        authorization = Authorization()
        excludes =['is_superuser', 'is_active', 'is_staff', 'password', 'last_login',]
        filtering = {
            'location': ('exact'),
        }

    def obj_create(self, bundle, **kwargs):
        bundle.data['username'] = bundle.data['email']
        return super(CustomerResource, self).obj_create(bundle, **kwargs)

I want to be able to make a POST to the API with some JSON gathered from a form (it can't just be form data because I need to do some processing) to create the user. Here is my Django code that posts up to the API:

    payload = {}
    payload['email'] = request.POST['username']
    payload['username'] = request.POST['username']
    payload['password1'] = request.POST['password1']
    payload['password2'] = request.POST['password2']
    payload = json.dumps(payload)

    customer = requests.post(self.base_url + '/v1/customers/', data=payload, headers=headers)

This posts a customer fine -- it exists in the API's database. However, its password for some reason is not registered. The password attribute is blank, whereas if I create a user locally in the API's database, the password appears as an encrypted hash. The has_usable_password field is set to false. The same thing happens if I try payload['password'] = request.POST['password1']. Any suggestions?

My Customer model:

class Customer(AbstractUser):

    current_location = models.ForeignKey('device.Location',
            null=True, blank=True, related_name='customers_present')
    default_location = models.ForeignKey('device.Location',
            null=True, blank=True, related_name='default_customers')

    def __unicode__(self):
        return u'{0}'.format(self.username)

Solution

  • Your Customer model has inherited a custom UserManager manager attribute named objects. It allows you to easily create Customer instances and takes care of the password encryption.

    You have to override the obj_create method in your CustomerResource:

    class CustomerResource(ModelResource):
    
        locations = fields.ToManyField('device.resources.LocationResource',
                'location_set', null=True)
        current_location = fields.ToOneField('device.resources.LocationResource',
                'current_location', null=True)
        default_location = fields.ToOneField('device.resources.LocationResource',
                'default_location', null=True)
    
        class Meta:
            queryset = Customer.objects.all()
            resource_name = 'customers'
            validation = CleanedDataFormValidation(form_class=RegistrationForm)
            list_allowed_methods = ['get', 'post']
            detail_allowed_methods = ['get', 'put', 'patch', 'delete']
            authorization = Authorization()
            excludes =['is_superuser', 'is_active', 'is_staff', 'password', 'last_login',]
            filtering = {
                'location': ('exact'),
            }
    
        def obj_create(self, bundle, **kwargs):
            bundle.obj = self._meta.object_class.objects.create_user(
                username=kwargs['username'],
                email=kwargs['email'],
                password=kwargs['password1'],
            )
            return bundle