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 m
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)
A complete example that support POST
and PUT
/PATCH
without another SQL UPDATE statement.
class MyUserSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = '__all__'
def create(self, validated_data):
if "password" in validated_data:
from django.contrib.auth.hashers import make_password
validated_data["password"] = make_password(validated_data["password"])
return super().create(validated_data)
def update(self, instance, validated_data):
if "password" in validated_data:
from django.contrib.auth.hashers import make_password
validated_data["password"] = make_password(validated_data["password"])
return super().update(instance, validated_data)
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 :-).
Edit: Fix syntax error
Edit2: jkatzer's comment makes a lot of sense. I don't have the environment set up at the moment to look at it, and i also don't recall if there was more changes I'd made to this solution that did not put in the post. But looking at it now I'm agreeing with this comment, should probably do something more in the lines of Alwx's answer....
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)