Reputation: 12890
I am wrote a serializer for the User model in Django with DRF:
the model:
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
from django.db import models
from django.utils.translation import ugettext
class BaseModel(models.Model):
# all models should be inheritted from this model
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class User(AbstractBaseUser, BaseModel):
username = models.CharField(
ugettext('Username'), max_length=255,
db_index=True, unique=True
)
email = models.EmailField(
ugettext('Email'), max_length=255, db_index=True,
blank=True, null=True, unique=True
)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ('email', 'password',)
class Meta:
app_label = 'users'
the serializer:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = ['email', 'username', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = super().create(validated_data)
user.set_password(validated_data['password'])
user.save()
return user
def update(self, user, validated_data):
user = super().update(user, validated_data)
user.set_password(validated_data['password'])
user.save()
return user
It works. But I probably do two calls instead of one on every create/update and the code looks a little bit weird(not DRY). Is there an idiomatic way to do that?
$python -V
Python 3.7.3
Django==2.2.3
djangorestframework==3.10.1
Upvotes: 0
Views: 503
Reputation: 88459
I hope this will solve the issue,
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = ['email', 'username', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
return models.User.objects.create_user(**validated_data)
def update(self, user, validated_data):
password = validated_data.pop('password', None)
if password is not None:
user.set_password(password)
for field, value in validated_data.items():
setattr(user, field, value)
user.save()
return user
The create_user()
method uses the set_password()
method to set the hashable password.
Upvotes: 1
Reputation: 1847
You can create your own user manager by overriding BaseUserManager
and use set_password()
method there. There is a full example in django's documentation. So your models.py
will be:
# models.py
from django.db import models
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
class MyUserManager(BaseUserManager):
def create_user(self, email, username, password=None):
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
username=username,
)
user.set_password(password)
user.save(using=self._db)
return user
class BaseModel(models.Model):
# all models should be inheritted from this model
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class User(AbstractBaseUser, BaseModel):
username = models.CharField(
ugettext('Username'), max_length=255,
db_index=True, unique=True
)
email = models.EmailField(
ugettext('Email'), max_length=255, db_index=True,
blank=True, null=True, unique=True
)
# don't forget to set your custom manager
objects = MyUserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ('email', 'password',)
class Meta:
app_label = 'users'
Then, you can directly call create_user()
in your serializer's create()
method. You can also add a custom update method in your custom manager.
# serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = ['email', 'username', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = models.User.objects.create_user(
username=validated_data['username'],
email=validated_data['email'],
password=validated_data['password']
)
return user
Upvotes: 1