I have a problem with simple user model.I wrote a test for email normalize function and in response i've got a " django.db.utils.IntegrityError: duplicate key value violates unique constraint "core_user_username_key"
DETAIL: Key (username)=() already exists. " When I added a "user.delete()"after "self.assertEqual(user.email, expected)" all test passed.What is wrong ? Is that test creates users with the same username field ?
Models.py
class UserManager(BaseUserManager):
"""Manager for user"""
def create_user(self, email, password=None, **extra_fields):
user = self.model(email=self.normalize_email(email), **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
class User(AbstractUser, PermissionsMixin):
"""User model"""
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = UserManager()
REQUIRED_FIELDS = []
USERNAME_FIELD = 'email'
test.py
class ModelTest(TestCase):
def test_create_user_with_email(self):
"""Testing creating user with email address"""
email = 'testaddress#example.com'
password = 'testpassword1234'
user = get_user_model().objects.create_user(
email=email,
password=password,
)
self.assertEqual(user.email, email)
self.assertTrue(user.check_password(password))
def test_user_email_normalized(self):
"""Testing email normalize function"""
test_emails = [
['test1#EXAMPLE.Com', 'test1#example.com'],
['TesT2#exaMple.com', 'TesT2#example.com'],
]
for email, expected in test_emails:
user = get_user_model().objects.create_user(email, 'password123')
self.assertEqual(user.email, expected)
user.delete()
It creates a user with the same username. You inherited this from the parent model, and did not remove the username field. You should specify this as None, so:
class User(AbstractUser, PermissionsMixin):
"""User model"""
username = None
# …
In the tests you thus do not have to delete the user: the system should be capable to register multiple users with each a different email.
Related
I am using DRF and I have these pieces of code as models, register view and serializer
But anytime I signup a user the password does not hashed and I can't see to figure out why.
models.py
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError("Users must have an email")
email = self.normalize_email(email).lower()
user = self.model(email=email, **kwargs)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password, **extra_fields):
if not password:
raise ValueError("Password is required")
user = self.create_user(email, password)
user.is_superuser = True
user.is_staff = True
user.save()
return user
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=255, unique=True)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
role = models.CharField(max_length=255)
department = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
is_verified = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = UserManager()
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["first_name", "last_name", "role", "department"]
def __str__(self):
return self.email
serializers.py
class RegisterSerializer(serializers.ModelSerializer):
email = serializers.CharField(max_length=255)
password = serializers.CharField(min_length=8, write_only=True)
first_name = serializers.CharField(max_length=255)
last_name = serializers.CharField(max_length=255)
role = serializers.CharField(max_length=255)
department = serializers.CharField(max_length=255)
class Meta:
model = User
fields = ["email", "password", "first_name", "last_name", "role", "department"]
def create(self, validated_data):
return User.objects.create(**validated_data)
def validate_email(self, value):
if User.objects.filter(email=value).exists():
raise serializers.ValidationError("This email already exists!")
return value
views.py
class RegisterView(APIView):
serializer_class = RegisterSerializer
def post(self, request, *args):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
user_data = serializer.data
user = User.objects.get(email=user_data.get("email"))
return Response(user_data, status=status.HTTP_201_CREATED)
for some reason, which I don't know anytime a user is created the password is save in clear text. It does not hash the passwords. The superuser's password is however hashed because I created it with the command line but the api doesn't hash the password. I some help to fix this.
Instead of passing plain password you should use make_password method provided by django.
from django.contrib.auth.hashers import make_password
make_password(password, salt=None, hasher='default')
Creates a hashed password in the format used by this application. It takes one mandatory argument: the password in plain-text (string or bytes). Optionally, you can provide a salt and a hashing algorithm to use, if you don’t want to use the defaults (first entry of PASSWORD_HASHERS setting). See Included hashers for the algorithm name of each hasher. If the password argument is None, an unusable password is returned (one that will never be accepted by check_password()).
You can try something like this
hashed_pass = make_password(your_password_here, salt=None, hasher='default')
user = self.create_user(email, hashed_pass)
Source: https://docs.djangoproject.com/en/4.1/topics/auth/passwords/
Try this:
def create(self, validated_data):
password = validated_data.pop('password')
user = User.objects.create(**validated_data)
user.set_password(password)
user.save()
return user
I found why it wasn't working. In the create method in the RegisterSerializer I did a return User.objects.create(**validated_data)
which saves on the request data in the database, including the password without hashing it first. The correct way is to be calling the create_user method instead, like so return User.objects.create_user(**validated_data) since that method has the set_password mechanism to hash the password.
I've created custom user model, and i'm trying to filter data in views.py by that user.
The error i get is:
'SomeClassView' object has no attribute 'user'
My goal is to 'encapsulate' data for each user.
user model:
class CustomUserManger(BaseUserManager):
use_in_migrations = True
def create_user(self, email, username, password, **other_fields):
email = self.normalize_email(email)
user = self.model(email=email, username=username, **other_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, username, password, **other_fields):
other_fields.setdefault('is_staff', True)
other_fields.setdefault('is_superuser', True)
other_fields.setdefault('is_active', True)
if other_fields.get('is_staff') is not True:
raise ValueError('Superuser must be assigned to staff')
if other_fields.get('is_superuser') is not True:
raise ValueError('Superuser must be assigned to superusers')
return self.create_user(email, username, password, **other_fields)
class User(AbstractUser, PermissionsMixin):
username = models.CharField(_('username'), max_length=20, unique=True)
email = models.EmailField(unique=True)
password = models.CharField(max_length=128)
is_staff = models.BooleanField(default=True)
is_active = models.BooleanField(default=True)
objects = CustomUserManger()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
def __str__(self):
return self.username
Views:
class SomeClassView(viewsets.ModelViewSet):
user = SomeClass.user
serializer_class = WagonSerializer
authentication_classes = (SessionAuthentication, )
#login_required
def get_queryset(self):
user = self.request.user
return SomeClass.objects.filter(user=user)
Ok, i forgot that my react app doesn't put user to json, so in database user was null, that's why table was blank.
I have created a model for an AbstractBaseUser, but am struggling to authenticate it.
Here is my model:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.contrib.auth.password_validation import validate_password
from uuid import uuid4
class CustomUserManager(BaseUserManager):
def _create_user(self, email, password, is_staff=False, is_superuser=False, **other_fields):
if not email:
raise ValueError('Email address must be specified')
if not password:
raise ValueError('Password must be specified')
user = self.model(
email=self.normalize_email(email),
is_staff=is_staff,
is_superuser=is_superuser,
**other_fields
)
validate_password(password)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email, password, **other_fields):
return self._create_user(email, password, False, False, **other_fields)
def create_superuser(self, email, password, **other_fields):
return self._create_user(email, password, True, True, **other_fields)
class CustomUser(AbstractBaseUser):
id = models.UUIDField(primary_key=True, default=uuid4(), editable=False, unique=True)
email = models.EmailField(max_length=254, unique=True)
is_superuser = models.BooleanField(default=True)
is_staff = models.BooleanField(default=True)
is_active = models.BooleanField(default=True)
is_premium = models.BooleanField(default=False)
first_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=40)
display_name = models.CharField(max_length=40)
date_of_birth = models.DateField()
currency = models.CharField(max_length=3)
date_joined = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
REQUIRED_FIELDS = ['first_name', 'display_name', 'date_of_birth', 'currency']
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
objects = CustomUserManager()
def get_full_name(self):
return ("%s %s" % (self.first_name, self.last_name)) if self.last_name != '' else self.first_name
def get_short_name(self):
return self.first_name
def __str__(self):
return "%s %s - %s" % (self.first_name, self.last_name, self.email)
Here is my view:
from django.contrib.auth import authenticate
from django.http import HttpResponse
def user_login(request):
user = authenticate(request, email=request.POST['email'], password=request.POST['password'])
if user is not None:
login(request, user)
else:
HttpResponse("Invalid email/password pair", status=401)
return HttpResponse()
And, in my settings.py, I have the following:
AUTH_USER_MODEL = 'myapp.CustomUser'
The issue is that, whenever I send create a user and send a request to user_login, the authenticate method always returns None, thus triggering the HttpResponseBadRequest. The email in the request matches what is in the database, and the password matches the password I used to create the user (I've spent longer than I care to admit using print statements to verify that).
Is there something I am missing? Do AbstractBaseUsers require something extra for authentication?
The comment by Iain Shelvington clued me into the answer.
The test I was using to test the authentication was creating the user by saving it directly to the database like this:
from .models import CustomUser
user_data = generate_user()
CustomUser(**user_data).save()
Because the user was being saved directly to the database, its password never got hashed.
I needed to instead create the user like this:
from Django.test import Client
from .models import CustomUser
user_data = generate_user()
client = Client()
client.post('/endpoint/used/to/create/user', user_data)
I have this problem for a month.
I'm using abstractbasemodel and basemanagerto to create a login and signup API using rest framework.
However, when I create a user password, it is saved as raw data since I use set_password() method and custom model manager confuses me...
This is my code :
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ('id' ,'email' ,'name' ,'password')
extra_kwargs = {
'password':{
'write_only':'True',
'style': {'input_type': 'password'}
}
}
def create(self, validated_data):
user = UserProfile.people.create_user(
email = validated_data['email'],
name = validated_data['name'],
password = validated_data['password']
)
class UserProfileViewSet(viewsets.ModelViewSet):
serializer_class = serializers.UserProfileSerializer
queryset = models.UserProfile.people.all()
authentication_classes = (TokenAuthentication, )
permission_classes = (UpdateOwnProfile, )
filter_backends = (SearchFilter, )
search_fields = ('name', 'email')
class UserLoginApiView(ObtainAuthToken):
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
class UserProfileManager(BaseUserManager):
def create_user(self, email, name, password=None):
print("user model manager")
if not email:
raise ValueError('User Must Have an Email Address')
email = self.normalize_email(email)
user = self.model(email=email, name=name )
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, name, password):
user = self.create_user(email, name, password)
user.is_superuser = True
user.is_staff = True
user.save(using=self._db)
return user
class UserProfile(AbstractBaseUser,PermissionsMixin):
email = models.EmailField(max_length=255,unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
people = UserProfileManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name']
def get_full_name(self):
return self.name
def get_short_name(self):
return self.name
def __str__(self):
return self.email
class Profile(models.Model):
user = models.OneToOneField(UserProfile,on_delete=models.CASCADE,relat ed_name="Profile")
location = models.CharField(max_length=100,blank=True,null=True)
bio = models.CharField(max_length=100,blank=True,null=True)
creationDate = models.DateTimeField(auto_now_add=True)
follower = models.ManyToManyField(UserProfile,related_name="Following",blank=True)
class Meta:
verbose_name='Profile'
verbose_name_plural='Profiles'
I also defined auth user model in settings :
AUTH_USER_MODEL='profiles.UserProfile'
to make sure Django uses my custom user model.
I don't know whats wrong as there is no error and only superusers that are created in terminal using manage.py are saved with hashed password.
Users which are created with my viewsets are saved with raw password.
First, I named the model manager "objects" and now, its people but the create user method wont run at all.
You can use django's built in hasher to create hashed password. It can be applied in .create method. First import from django.contrib.auth.hashers import make_password and then modify .create() method,
def create(self, validated_data):
user = UserProfile.people.create_user(
email = validated_data['email'],
name = validated_data['name'],
password = make_password(validated_data['password']) # here
)
return user
Or
if you don't override the .create() method then add the following validate_password method in serializer,
The validate_password is ran, everytime a new object has to be created
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ('id' ,'email' ,'name' ,'password')
extra_kwargs = {
'password':{
'write_only':'True',
'style': {'input_type': 'password'}
}
}
def validate_password(self, value: str) -> str:
return make_password(value)
I'm trying to create a user update endpoint that only updates select fields of a user. My currently implementation is to save the model with ModelForm's save method but I've also tried with the base model save method.
My request body doesn't contain a password field and I don't have a password field in my UserForm, however I'm unable to maintain the current session or logout and log back in with the current user after executing an update. I'm nearly positive this is because Django is changing my password somewhere during the update process.
Other pertinent information: I'm using Django (not Django Rest Framework). I have a custom user model. I know I can solve this issue by using serializers with DRF but it seems hacky to use that for this issue alone.
My endpoint looks like this:
def saveAccountData (request):
resp = {}
if request.method == 'POST':
form = UserForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
resp['result'] = 'User updated'
return HttpResponse(
json.dumps(resp),
status=200
)
else:
return HttpResponse(
json.dumps(form.errors),
status=422,
)
My Form looks like this:
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ['email', 'name', 'is_active', 'is_admin']
def save(self, commit=True):
user = super(UserForm, self).save(commit=False)
user.name = self.cleaned_data['name']
if commit:
user.save()
return user
User model:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.conf import settings
import logging
class UserManager(BaseUserManager):
def create_user(self, email, name, password=None):
# executes if email is zero (false) or an empty container (equivalent to None)
logger = logging.getLogger(__name__)
logger.warning('Create user: ' + password)
if not email:
raise ValueError("Please provide an email address")
if not password:
raise ValueError("Please enter a password")
if not name:
raise ValueError("Please enter your name")
# normalize email address (convert to lower case so all email addresses are standardized)
user = self.model(
email = self.normalize_email(email),
name=name,
)
# the set_password method encrypts the password
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password, name):
user = self.create_user(
email,
password=password,
name=name)
user.is_superuser = True
user.is_admin = True
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=50, unique=True)
password = models.CharField(max_length=100)
name = models.CharField(max_length=100)
phone = models.CharField(max_length=20)
city = models.CharField(max_length=50)
state = models.CharField(max_length=20)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = UserManager()
# we must specify a username field
USERNAME_FIELD = 'email'
# required fields are used for create_superuser
REQUIRED_FIELDS = ['name']
def get_full_name(self):
return self.name
def get_short_name(self):
return self.name
def __str__(self):
# django uses this method when it needs to convert an object to a string
return self.email
I created a UpdateAccountForm(forms.ModelForm),
and overwrite save() like:
```
class Meta:
model = User
fields = ['username', 'groups', 'is_active']
def save(self, commit=True):
┆ user = super(UpdateAccountForm, self).save(commit=False)
┆ password = self.cleaned_data["password"]
┆ if password:
┆ ┆ user.set_password(password)
┆ if commit:
┆ ┆ user.save()
┆ return user
```