在Django管理界面中更改密码

19
我最近根据Django项目文档创建了admin.py文件:
https://docs.djangoproject.com/en/dev/topics/auth/customizing/#django.contrib.auth.models.AbstractBaseUser 但是我真的很想要管理员能够更改用户密码的功能。如何添加此功能?我只是复制并粘贴了上面链接中的代码。
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField

from customauth.models import MyUser


class UserCreationForm(forms.ModelForm):
    """A form for creating new users. Includes all the required
    fields, plus a repeated password."""
    password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
    password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)

    class Meta:
        model = MyUser
        fields = ('email', 'date_of_birth')

    def clean_password2(self):
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super(UserCreationForm, self).save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user


class UserChangeForm(forms.ModelForm):
    """A form for updating users. Includes all the fields on
    the user, but replaces the password field with admin's
    password hash display field.
    """
    password = ReadOnlyPasswordHashField()

    class Meta:
        model = MyUser

    def clean_password(self):
        # Regardless of what the user provides, return the initial value.
        # This is done here, rather than on the field, because the
        # field does not have access to the initial value
        return self.initial["password"]


class MyUserAdmin(UserAdmin):
    # The forms to add and change user instances
    form = UserChangeForm
    add_form = UserCreationForm

    # The fields to be used in displaying the User model.
    # These override the definitions on the base UserAdmin
    # that reference specific fields on auth.User.
    list_display = ('email', 'date_of_birth', 'is_admin')
    list_filter = ('is_admin',)
    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        ('Personal info', {'fields': ('date_of_birth',)}),
        ('Permissions', {'fields': ('is_admin',)}),
        ('Important dates', {'fields': ('last_login',)}),
    )
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'date_of_birth', 'password1', 'password2')}
        ),
    )
    search_fields = ('email',)
    ordering = ('email',)
    filter_horizontal = ()

# Now register the new UserAdmin...
admin.site.register(MyUser, MyUserAdmin)
# ... and, since we're not using Django's builtin permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)

[更新 - 添加信息] 我更改了以下信息,但仍然只在一个只读字段中看到密码(加密)。如何添加更改密码的链接?

fieldsets = (
    ('Permissions', {'fields': ('is_active', 'is_admin','password')}),
)
add_fieldsets = (
    (None, {
        'classes': ('wide',),
        'fields': ('email', 'password')}
    ),
)
8个回答

69

将此内容添加至您的UserChangeForm中:

password = ReadOnlyPasswordHashField(label=("Password"),
        help_text=("Raw passwords are not stored, so there is no way to see "
                    "this user's password, but you can change the password "
                    "using <a href=\"../password/\">this form</a>."))

1
有人知道为什么我尝试访问.../user/#id/password/时会得到404吗?我该怎么做才能获得我的自定义用户模型的管理表单? - Dustin
5
找到了答案:“如果你的自定义用户模型扩展自 django.contrib.auth.models.AbstractUser,你可以使用 Django 中现有的 django.contrib.auth.admin.UserAdmin 类。但是,如果你的用户模型扩展自 AbstractBaseUser,你需要定义一个自定义的 ModelAdmin 类。可能可以继承默认的 django.contrib.auth.admin.UserAdmin,但是你需要覆盖任何引用 django.contrib.auth.models.AbstractUser 上不存在于你的自定义 User 类上的字段的定义。” - Dustin
16
同意。我只需要将最后一行更改为:'使用<a href=\'../password/\'>此表单</a>。'在django 1.9.1中,请注意在'密码'之前的**../**。 - oskargicast
1
感谢@oskargicast的评论,它对我有所帮助,特别是在1.9版本方面。 - neosergio
1
我无法在Django 2.1中成功。你还需要改变什么? - Twimnox
显示剩余3条评论

14
password = ReadOnlyPasswordHashField(label= ("Password"),
        help_text= ("Raw passwords are not stored, so there is no way to see "
                    "this user's password, but you can change the password "
                    "using <a href=\"../password/\">this form</a>."))

链接地址有所变化,对于早期版本的 Django,你可以使用:

<a href=\"/password/\">这个表单</a>.

对于 Django 1.9+,请使用 <a href=\"../password/\">这个表单</a>


12

我在我的UserAdmin类中添加了这个方法:

def save_model(self, request, obj, form, change):
    # Override this to set the password to the value in the field if it's
    # changed.
    if obj.pk:
        orig_obj = models.User.objects.get(pk=obj.pk)
        if obj.password != orig_obj.password:
            obj.set_password(obj.password)
    else:
        obj.set_password(obj.password)
    obj.save()
你可以正常显示密码字段,但管理员只能看到散列密码。如果他们更改它,则新值将被哈希并保存。
每次通过管理员保存用户时会添加一个单独的查询。通常不会出现问题,因为大多数系统没有管理员密集编辑用户。

我刚意识到你可以使用change的值而不是if obj.pk。这是留给读者的练习。 ;) - WhyNotHugo
1
我喜欢这个解决方案,因为它意味着我不必去处理子类化表单。你还可以使用 form.changed_data 来使它更加简洁。 - Justin

3

如果您需要一种与Django版本无关的解决方案,可以在UserChangeForm.__init__中使用类似以下代码的reverse函数反转URL:

from django.core.urlresolvers import reverse

class UserChangeForm(forms.ModelForm):
    password = ReadOnlyPasswordHashField()
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['password'].help_text = (
            "Raw passwords are not stored, so there is no way to see "
            "this user's password, but you can <a href=\"%s\"> "
            "<strong>Change the Password</strong> using this form</a>."
        ) % reverse_lazy('admin:auth_user_password_change', args=[self.instance.id])

这是正确的解决方案。 - kloddant

2
你可以考虑通过以下方式扩展UserAdmin
from django.contrib import admin
from myapp.models import CustomUser
from django.contrib.auth.admin import UserAdmin

class CustomUserAdmin(UserAdmin):
    list_display = []
admin.site.register(CustomUser, CustomUserAdmin)

这个回答被低估了。 - Sebastian Wagner

2

您也可以这样做,这样您只需要在密码字段上编写并保存后,它将为其创建哈希:

class UserModelAdmin(admin.ModelAdmin):

    """
        User for overriding the normal user admin panel, and add the extra fields added to the user
        """


def save_model(self, request, obj, form, change):
    user_database = User.objects.get(pk=obj.pk)
    # Check firs the case in which the password is not encoded, then check in the case that the password is encode
    if not (check_password(form.data['password'], user_database.password) or user_database.password == form.data['password']):
        obj.password = make_password(obj.password)
    else:
        obj.password = user_database.password
    super().save_model(request, obj, form, change)

我需要捕获异常user_database.DoesNotExist,用于第一次创建用户。 - j4n7

0

只需在您的类表单中删除“密码”输入:

class MyUserChangeForm(forms.ModelForm):
# password = forms.CharField(label='Password', required=True, widget=forms.PasswordInput)

# password = ReadOnlyPasswordHashField()

class Meta:
    model = CustomUser
    fields = '__all__'


def save(self, commit=True):
    user = super().save(commit=False)
    user.set_password(self.cleaned_data["password"])
    if commit:
        user.save()
    return user

Django 3.2.8


1
目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

0
('Permissions', {'fields': ('is_active', 'is_superuser',)}),

你好 Catherine,请查看我的问题更新。我能够在只读字段中看到密码。但是,我无法选择新密码。 - Thomas
@Thomas 我追踪了代码,发现密码变成只读是因为 ReadOnlyPasswordHashField()。解决方案是,在密码只读字段下创建一个链接,该链接指向更改密码表单。 - catherine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接