How To Create Users Without Setting Their Password

When creating a new user through Django’s admin interface, you are asked to enter the new user’s password. This is less than ideal, because it requires the admin to think of a password for someone else, communicate it to them somehow, and then the user must remember to change it. A better way would be to send a password-reset email to the new user, allowing them to enter their own password.

To implement this, we need to provide a user-creation form that has an optional (instead of required, like the built-in form) password field and a User admin that uses the form and sends the password-reset email when creating a new user.

We’ll subclass UserCreationForm to create a form with optional password fields:

from django import forms
from authtools.forms import UserCreationForm

class UserCreationForm(UserCreationForm):
    A UserCreationForm with optional password inputs.

    def __init__(self, *args, **kwargs):
        super(UserCreationForm, self).__init__(*args, **kwargs)
        self.fields['password1'].required = False
        self.fields['password2'].required = False
        # If one field gets autocompleted but not the other, our 'neither
        # password or both password' validation will be triggered.
        self.fields['password1'].widget.attrs['autocomplete'] = 'off'
        self.fields['password2'].widget.attrs['autocomplete'] = 'off'

    def clean_password2(self):
        password1 = self.cleaned_data.get("password1")
        password2 = super(UserCreationForm, self).clean_password2()
        if bool(password1) ^ bool(password2):
            raise forms.ValidationError("Fill out both fields")
        return password2

Then an admin class that uses our form and sends the email:

from django.contrib.auth import get_user_model
from django.contrib.auth.forms import PasswordResetForm
from django.utils.crypto import get_random_string
from authtools.admin import NamedUserAdmin

User = get_user_model()

class UserAdmin(NamedUserAdmin):
    A UserAdmin that sends a password-reset email when creating a new user,
    unless a password was entered.
    add_form = UserCreationForm
    add_fieldsets = (
        (None, {
            'description': (
                "Enter the new user's name and email address and click save."
                " The user will be emailed a link allowing them to login to"
                " the site and set their password."
            'fields': ('email', 'name',),
        ('Password', {
            'description': "Optionally, you may set the user's password here.",
            'fields': ('password1', 'password2'),
            'classes': ('collapse', 'collapse-closed'),

    def save_model(self, request, obj, form, change):
        if not change and (not form.cleaned_data['password1'] or not obj.has_usable_password()):
            # Django's PasswordResetForm won't let us reset an unusable
            # password. We set it above super() so we don't have to save twice.
            reset_password = True
            reset_password = False

        super(UserAdmin, self).save_model(request, obj, form, change)

        if reset_password:
            reset_form = PasswordResetForm({'email':})
            assert reset_form.is_valid()

Using django.contrib.auth.forms.PasswordResetForm allows us to share the email-sending code with Django. If you wanted to change the template the email uses, email_template_name would be the place to do it.

Now we can replace the installed UserAdmin with our own.

from django.contrib import admin, UserAdmin)

You can view the complete file here.