I had a lot of trouble recently implementing a User and related objects 
using the django.contrib.auth get_user_model() within the DRF. Much of my 
trouble came from migrating from DRF 2.3.x to 3.3.3. I found myself in a 
spot where my password was being stored in plaintext in the database, even 
though my UserSerializer class create looked as follows
class UserSerializer(serializers.ModelSerializer):
...
    def create(self,validated_data):
         survey_id = self.initial_data['profile']['survey_id']
                  user = get_user_model().objects.create(**validated_data)
         user.set_password(validated_data['password'])
         user.save()
         saved_profile = 
Profile.objects.create(user=user,survey_id=survey_id)
         Collection.objects.create(creator=saved_profile)
         Token.objects.create(user=user)
         return user
...
and in my views.py, I had a perform_create method, roughly as follows
class UserCreateView(CreateAPIView):
    model = get_user_model()
    serializer_class = UserSerializer

    def perform_create(self,serializer):
        serializer.save(rando_user_attribute=self.get_rando_attribute(arg))

I had this because it wasn't clear to me if perform_create replaced 
pre_save or post_save in DRF 2.x.

For the case I illustrate above, we should be able to quickly see how I 
ended up with plaintext passwords in my database:
Even though the serializer class was creating hte User object and calling 
that instance's set_password() method, when the serializer's create() 
method returned to perform_create, the serializer.save() method was 
clobbering the instance data with effectively stale data from data the 
serializer instance had before the serializer class's create() method was 
called. So password was being set back to what the serializer class would 
have had for validated_data['password'].

So the docs are write, this does replace pre_save and post_save, just not 
in a very clear way. In fact, it's more like post_save using pre_save data. 
Arrgh.

My own recco for the docs is to be clear that this perform_create really 
replaces post_save(), and one should be careful using serializer.save() 
within perform_create() because it might save data back to your model 
instance that had not been intended to be saved without some prior 
transformation. Alternatively, perhaps the docs should recco that the 
serializer really just be there as a convenience for ingesting input data 
and validation, and that actual db model instnace create() calls should be 
placed only in the perform_create() method and that the serializer class 
create() method should not hold such calls.

Thoughts?

Thanks,
Steve


-- 
You received this message because you are subscribed to the Google Groups 
"Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to