首页 > 解决方案 > 以 UUID 字段作为主键的 Django 管理员 UserChangeForm

问题描述

我正在开发一个包含敏感用户数据的 Django 应用程序,因此我正在采取一些步骤来匿名用户对象及其数据。

我创建了子类 AbstractBaseUser 模型的自定义“用户”对象,如下所示:

class User(AbstractBaseUser, PermissionsMixin):
    (...)
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    (...)

它具有以下链接ModelAdmin对象:

from django.contrib.auth.forms import UserChangeForm


@admin.register(User)
class UserAdmin(admin.ModelAdmin):
    form = UserChangeForm

我使用 UUID 字段作为主键以确保最大程度的匿名性,但我希望能够在 Django Admin 中重置用户密码(就像你可以使用默认User对象一样)

但是,当我在管理员中打开用户并按链接更改密码时,我收到以下错误消息:

User with ID "21362aca-6918-47ea-9b29-275350a89c54/password" doesn't exist. Perhaps it was deleted?

管理员 url 仍然需要一个以整数作为其pk值的 url。

所以看来我必须覆盖 ModelAdmin 定义中的管理 url 配置,但我想知道是否有更简单的方法来实现所需的结果 - 因为我想用 UUID 字段替换 User.pk 是相当常规的发生,我想象许多开发人员都遇到了这个问题。我试图找到某种设置/切换来实现这一点,但无济于事,我错过了什么吗?

标签: pythondjangodjango-admin

解决方案


您的 'UserAdmin' 继承自ModelAdmin,它通过get_urls添加、更改、删​​除等视图的方法提供 url,但也提供 'fallback' url:

urlpatterns = [
    #...
    url(r'^(.+)/change/$', wrap(self.change_view), name='%s_%s_change' % info),
    # For backwards compatibility (was the change url before 1.9)
    path('<path:object_id>/', wrap(RedirectView.as_view(
        pattern_name='%s:%s_%s_change' % ((self.admin_site.name,) + info)
    ))),
]

您关注的 url 看起来/user/<UUID>/password/只适合后备模式的正则表达式 - 它会将您重定向到更改页面,使其<UUID>/password用作object_id.

尝试从继承,django.contrib.auth.admin.UserAdmin因为它的get_urls方法提供了您需要的 url 模式。


更多的探索...
如果您的主键字段是 AutoField,则整个过程会ValueError('invalid literal for int')在尝试强制转换int('some_integer/password')django.db.models.fields.AutoField.get_prep_value准备查询值时引发 a。可以理解!

HOWEVERUUIDField使用get_prep_value基类的方法FieldField.get_prep_value只是简单地返回值,甚至不检查(毕竟,验证值应该是工作UUIDField)。因此,您最终会得到一个查找虚假 uuid 的查询'<uuid>/password',这显然不存在。


推荐阅读