[Fixed]-Django create new user without password

16👍

Programmatically, you can create/save a new User without a password argument, and it will not raise any exceptions. In fact, you can even create a user without any arguments. This is explained here.

The trick is to use create() instead of create_user(), as discussed here.

For example, you can do this in your code:

User.objects.create(username='user without password')

The database field password will then contain an empty string. Because the user now exists, the admin will show the UserChangeForm, and the password will show up as a ReadOnlyPasswordHashField which says "No password set.", as you can see in the image below. You can proceed normally from there.

screenshot of admin site

Note that this empty password is not the same as an unusable password. The PasswordResetView does not work with an unusable password, but it does work with an empty password.

Also note that User.objects.create(password='') works, but User.objects.create(password=None) does not: the latter raises a IntegrityError due to the NOT NULL constraint in the database.

Just to illustrate the options, here’s a test with different ways to create a user:

from django.test import TestCase
from django.contrib.auth.models import User
from django.contrib.auth.hashers import make_password
import django.db
import django.db.transaction

RAW_PASSWORD = 'mypassword'


class UserTests(TestCase):
    def tearDown(self) -> None:
        # print passwords as stored in test database
        for user in User.objects.all():
            print(f'username: {user.username}\tpassword: {user.password}')

    def test_user_create_missing_required_fields(self):
        # none of these raise any exceptions, despite required model fields
        user_kwargs = [dict(),
                       dict(username='a'),
                       dict(username='b', password=''),
                       dict(username='c', password=RAW_PASSWORD)]
        for kwargs in user_kwargs:
            user = User.objects.create(**kwargs)
            # password is "usable" ...
            self.assertTrue(user.has_usable_password())
            if 'password' in kwargs:
                # stored password is still raw (not hashed)
                self.assertEqual(kwargs['password'], user.password)
                # ... but password-check fails
                self.assertFalse(user.check_password(kwargs['password']))

    def test_user_set_password(self):
        # can set a usable password after user creation
        user = User(username='d')
        user.set_password(RAW_PASSWORD)  # does not save...
        user.save()
        self.assertTrue(user.has_usable_password())
        self.assertNotEqual(RAW_PASSWORD, user.password)
        self.assertTrue(user.check_password(RAW_PASSWORD))

    def test_user_create_hashed_password(self):
        # we can initialize with a hashed password
        hashed_password = make_password(RAW_PASSWORD)
        user = User.objects.create(username='e', password=hashed_password)
        self.assertTrue(user.has_usable_password())
        self.assertTrue(user.check_password(RAW_PASSWORD))

    def test_user_set_unusable_password(self):
        # can set an unusable password
        user = User(username='f')
        user.set_unusable_password()  # does not save...
        user.save()
        self.assertFalse(user.has_usable_password())

    @django.db.transaction.atomic
    def test_user_create_password_none(self):
        # cannot initialize with password=None (violates the NOT NULL constraint)
        with self.assertRaises(django.db.IntegrityError) as a:
            User.objects.create(username='g', password=None)
        self.assertIn('not null', str(a.exception).lower())

    def test_user_set_password_none(self):
        # can set None in set_password(), but that becomes unusable
        user = User(username='h')
        user.set_password(None)
        user.save()
        self.assertFalse(user.has_usable_password())

    def test_user_force_login(self):
        # for testing, we can use force_login(),
        # even if the user is created without arguments
        user_without_args = User.objects.create()
        self.client.force_login(user=user_without_args)
        self.assertTrue(user_without_args.is_authenticated)
👤djvg

13👍

As you described, you modified the form fields to not require password, but what about the model? The build-in User model enforces use of password (it is required model field), and will give errors if you try to save a User objects without one. There’s a special method for when you don’t want to set a real password – django.contrib.auth.models.User.set_unusable_password(). Use it in your view before saving the form data.

👤Nikita

Leave a comment