Using django-rest-framework 3 and django 1.8
I am trying to create a user using django-rest-framework ModelViewSerializer. problem is that the default objects.create method used by DRF leave the password as plain text.
The problem is that DRF serialzer create method is using objects.create
querysets/#create method instead of using objects.create_user
method.
code from serializers.py line 775
instance = ModelClass.objects.create(**validated_data)
What is the best solution for this? i can override the serializer.create method to use objects.user_create instead of objects.create but it does not feel like the right solution.
rest of code:
from django.contrib.auth.models import User
from rest_framework import viewsets
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'email','password')
write_only_fields = ('password',)
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer = UserSerializer()
you can override create
in UserSerilizer:
class UserSerializer(serializers.ModelSerializer):
# ....
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
return user
other solutions can be overriding perform_create
in ViewSet class or you can write your own create
method in your viewset class
class UserViewSet(viewsets.ModelViewSet):
def create(self, request, format=None):
# create user here
# do not call seriailzer.save()
UPDATE: after @freethebees commented, overriding perform_create
also works, so here is the code snippet:
class UserViewSet(viewsets.ModelViewSet, mixins.CreateModelMixin):
def perform_create(self, serializer):
# use User.objects.create_user to create user
pass
NOTE:
this answer gives 3 solutions, choose the one you think it better fits your needs and your project's ecosystem
NOTE 2
I personally prefer overriding create
in UserViewSet
(second code snippet) because there you can simply return your custom Response
(for example return user profile after login)
There is even better option to validate password in serializer
from django.contrib.auth.hashers import make_password
class UserSerializer(serializers.ModelSerializer):
def validate_password(self, value: str) -> str:
return make_password(value)
In addition to @aliva's answer where you miss out on the functionalities in serializers.Modelserializer.create()
(which could be quite nice to preserve, for example handling of many-to-many relations), there is a way to keep this.
By using the user.set_password()
method, the password can also be correctly set, like:
class UserSerializer(serializers.ModelSerializer):
def create(self, validated_data):
user = super().create(validated_data)
user.set_password(validated_data['password']
user.save()
return user
This has the benefit of keeping the super class' functionality, but the downside of an additional write to the database. Decide which trade-off is more important to you :-).