Creating user with DRF - setting password with custom serializer

1,401 views
Skip to first unread message

Victor Hooi

unread,
Aug 5, 2013, 10:57:06 PM8/5/13
to django-res...@googlegroups.com
Hi,

I have a Django app using a custom User model called Customer:

class Customer(AbstractUser):
    GENDER_CHOICES = (
        ('M', 'Male'),
        ('F', 'Female'),
    )
    gender = models.CharField(max_length=1, choices=GENDER_CHOICES, null=True, blank=True)
    birth_year = models.PositiveSmallIntegerField(null=True, blank=True)
    class Meta:
        ordering = ['last_name', 'first_name']
    def __unicode__(self):
        return self.username

I'm using Django REST framework to provide an API to add users:

class CustomerList(generics.ListCreateAPIView):
    queryset = Customer.objects.all()
    serializer_class = CustomerSerializer

I'm basing my serializer from code from Tom Christie from here:


My custom serializer:

class CustomerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Customer
        fields = ('username', 'password', 'first_name', 'last_name', 'email', 'is_active', 'gender', 'birth_year')

    def restore_object(self, attrs, instance=None):
        if instance:
            customer = instance
            customer.username = attrs['username']
            customer.first_name = attrs['first_name']
            customer.last_name = attrs['last_Name']
            customer.email = attrs['email']
            if attrs['is_active']:
                customer.is_active = attrs['is_active']
            if attrs['gender']:
                customer.gender = attrs['gender']
            if attrs['birth_year']:
                customer.birth_year = attrs['birth_year']
        else:
            customer = Customer(username = attrs['username'],
                                first_name = attrs['first_name'],
                                last_name = attrs['last_name'],
                                email = attrs['email']
                                )
        customer.set_password(attrs['password'])
        customer.save()
        return customer

First question - when I try to POST to this, I get:

IntegrityError at /api/v1/customers/
duplicate key value violates unique constraint "customers_customer_pkey"
DETAIL:  Key (id)=(6) already exists.

The weird thing - the user seems to have been created - however I still get the above error?

Second question - how could I handle optional fields like email, or gender or birth_year - these may or may not be in the POST, but I was hoping for a cleaner way to handle updating/creating users with these fields?

Cheers,
Victor

 

Victor Hooi

unread,
Aug 7, 2013, 4:35:27 AM8/7/13
to django-res...@googlegroups.com
Hi,

Ok, I've replaced the above custom serializer with overriding pre_save on the view instead:

class CustomerList(generics.ListCreateAPIView):
    queryset = Customer.objects.all()
    serializer_class = CustomerSerializer
    def pre_save(self, obj):
        obj.password = make_password(obj.password)

From my testing, this seems to work correctly, and as a side effect, is a lot less lines of code.

Can anybody see any reasons why this might be un-idiomatic (for Django or DRF), or why I might not want to do the above? Any security issues at all?

Cheers,
Victor

Tom Christie

unread,
Aug 8, 2013, 3:57:42 AM8/8/13
to django-res...@googlegroups.com
> Second question - how could I handle optional fields like email, or gender or birth_year - these may or may not be in the POST, but I was hoping for a cleaner way to handle updating/creating users with these fields?

Sure, If you want to keep the update logic in the serializer class, you could rewrite it something like this...

class CustomerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Customer
        fields = (
            'username', 'password', 'first_name', 'last_name',
            'email', 'is_active', 'gender', 'birth_year'
        )

    def restore_object(self, attrs, instance=None):
        password = attrs.pop('password', None)

        if instance:
            # Update an existing customer
            for key, val in attrs:
                setattr(customer, key, val)
        else:
            # Create a new customer
            customer = Customer(**attrs)

        if password:
            customer.set_password(password)

        customer.save()
        return customer

Hope that helps,

  Tom
Reply all
Reply to author
Forward
0 new messages