自定义用户模型错误

8

我正在尝试在Django中设置自定义用户模型。原因是我想使用电子邮件作为用户名,并完全删除用户名字段。我遇到了一个错误,我无法解决。

Manager isn't available; User has been swapped for 'app.MyUser'
Exception Location: .../django/db/models/manager.py in __get__, line 256
Python Version: 2.7.3

Python Path:    
[...project specific files,
'/usr/lib/python2.7',
'/usr/lib/python2.7/plat-linux2',
'/usr/lib/python2.7/lib-tk',
'/usr/lib/python2.7/lib-old',
'/usr/lib/python2.7/lib-dynload',
'/usr/local/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages/PIL',
'/usr/lib/python2.7/dist-packages/gtk-2.0',
'/usr/lib/pymodules/python2.7',
'/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode']

我已经疯狂地搜索了谷歌,但并没有找到太多关于这个错误信息的页面。我发现了一些页面,提供了解决方案的建议,但是这些建议都没有对我起作用。
我的代码:我已经设置了自定义用户模型。我在settings.py中声明了自定义用户模型AUTH_USER_MODEL = 'app.MyUser'。我还设置了一个自定义的UserManager:
class MyUserManager(BaseUserManager):

    def create_user(self, email, password=None):
        """
        Creates and saves a User with the given email. Note that none of the optional fields gets values in the creation. These fields will have to be filled out later on.
        """

        if not email:
            raise ValueError('Users must have an email address')

        user = self.model(email=MyUserManager.normalize_email(email))

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password=None):
        """
        Creates and saves a superuser with the the above mentioned attributes

        """

        user = self.create_user(email, password=password)
        user.is_admin = True
        user.save(using=self._db)
        return user


class MyUser(AbstractBaseUser, PermissionsMixin):
    """
    Custom made User model. No username, instead email is used as unique field and index
    """

    Genders = (('M', 'Man'), ('K', 'Woman'))    
    FirstName = models.CharField(max_length=30)
    LastName = models.CharField(max_length=40)
    Gender = models.CharField(max_length=2, choices=Genders, default='K')
    email = models.EmailField(verbose_name='email address', max_length=255, unique=True, db_index=True,)
    twitter = models.CharField(max_length=30)
    is_admin = models.BooleanField(default=False)


    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    def get_full_name(self):
        # The user is identified by their email address
        return self.email

    def get_short_name(self):
        # The user is identified by their email address
        return self.email

    def __unicode__(self):
        return self.email

    objects = MyUserManager()

我尝试过声明不同类型的UserAdmins,但没有任何效果。我尝试的第一个是:
class MyUserAdmin(UserAdmin):
    # The forms to add and change user instances
    #form = UserChangeForm
    #add_form = FrontpageRegistrationForm

    list_display = ('email', 'FirstName', 'LastName', 'Gender', 'twitter')
    list_filter = ()

    add_fieldsets = ((None, {'classes': ('wide',),'fields': ('email', 'password1', 'password2')}),)

    search_fields = ('email',)
    ordering = ('email',)
    filter_horizontal = ()

admin.site.register(MyUser, MyUserAdmin)

我已经将add_formform这两个属性注释掉,因为它们引发了一些表单错误,我想稍后再解决。

读到这里可能有一种解决方法后,第二个UserAdmin被创建。然而,这并没有帮助到当前的情况;

class MyUserAdmin(admin.ModelAdmin):
    # The forms to add and change user instances
    #form = UserChangeForm
    add_form = FrontpageRegistrationForm
    add_fieldsets = ((None, {'classes': ('wide',),'fields': ('email', 'password1', 'password2')}),)

    def get_fieldsets(self, request, obj=None):
        if not obj:
            return self.add_fieldsets
        return super(MyUserAdmin, self).get_fieldsets(request, obj)

    def get_form(self, request, obj=None, **kwargs):
        defaults = {}
        if obj is None:
            defaults.update({'form': self.add_form,'fields': admin.util.flatten_fieldsets(self.add_fieldsets),})

    defaults.update(kwargs)
    return super(MyUserAdmin, self).get_form(request, obj, **defaults)

我尝试删除数据库中的所有表,但没有成功。

我会永远感激任何人甚至看一眼这个问题。如果有人解决了这个问题,我会尽力说服我的妻子以第一个给我提供解决方案的Avatar的名字来命名我们的第一个孩子,这样我就可以继续过我的生活。

编辑:我尝试将AUTH_USER_MODEL设置为mainfolder.app.MyUser,我确信"mainfolder"在pythonpath上。init.py应该是正确的。新的settings.py给出了以下服务器错误:auth.user: AUTH_USER_MODEL不符合'app_label.app_name'的格式。admin.logentry: 'user'与模型smartflightsearch.SFSdb.MyUser有关联,该模型未被安装或为抽象模型。registration.registrationprofile: 'user'与模型有关联,该模型未被安装或为抽象模型。这是一个我不知道如何解释的新线索。


你的应用程序名称是 'app' 吗? - Rohan
首先,不要惊慌,我们会解决这个问题。 :) 你什么时候看到这个错误?如果禁用管理员,错误是否消失?你尝试过逐步调试模式吗? - Ivan Kharlamov
@Ivan 好的,很高兴听到这个消息 :) 在输入用户凭据并按下“登录”按钮后,错误会发生在 /admin 页面上。如果我关闭管理员权限,我希望错误会消失,但现在没有机会测试。不过与管理员无关的页面都可以正常工作。 - Rookie
@Rohan 不,我在发布时稍微改了一下代码以保护隐私,可能有点多虑,但无论如何。 - Rookie
我认为我们可以将问题缩小到您的“MyUserAdmin”表单上。当您提交问题时,您的“AUTH_USER_MODEL”已正确声明。您尝试过这个修复方法了吗? - Ivan Kharlamov
@Rookie,在确保FrontpageRegistrationFormMeta中有model = get_user_model()后,您是否仍然遇到错误? - Ivan Kharlamov
2个回答

10

TL;DR: 在下面这个答案的Solution部分使用代码。

详细解释: 你知道,从Django 1.5开始,仅仅子类化Django的UserAdmin是不足以与可交换的用户模型进行交互的:你还需要相应地覆盖表单。

如果你跳到django.contrib.auth.admin源代码中,你会看到UserAdminformadd_form有以下值:

# django/contrib/auth/admin.py
class UserAdmin(admin.ModelAdmin):
    ...
    form = UserChangeForm
    add_form = UserCreationForm 

这些表格指向 django.contrib.auth.forms遵守可交换用户模型的表格:

# django/contrib/auth/forms.py
class UserCreationForm(forms.ModelForm):
    ...
    class Meta:
        model = User  # non-swappable User model here.

class UserChangeForm(forms.ModelForm):
    ...
    class Meta:
        model = User  # non-swappable User model here.

解决方案: 因此,您应该遵循一个已经存在的伟大的答案不要忘记给它投票!)它归结为以下内容:

from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm, UserChangeForm

class MyUserChangeForm(UserChangeForm):
    class Meta:
        model = get_user_model()

class MyUserCreationForm(UserCreationForm):
    class Meta:
        model = get_user_model()

class MyUserAdmin(UserAdmin):
    form = MyUserChangeForm
    add_form = MyUserCreationForm

admin.site.register(MyUser, MyUserAdmin)

希望在未来的Django版本中可以解决这个问题(这是错误追踪器中相应的工单)。


1
亲爱的@Ivan,老实说我无法相信你有多么好心,花费了这么多时间和精力来帮助我解决问题。我真的很感激你的付出!如果每个用户都能发放一次某种英雄徽章,那该多好啊。如果有这样的事情存在,我会把这个徽章给你!今天我没有时间处理这个问题,我唯一尝试做的就是从你的解决方案中复制表格和UserAdmin。这引发了一个错误消息,但似乎是由于User模型中的所有字段都没有在更改/创建表单中声明所导致的。明天再告诉你吧。 - Rookie
1
非常感谢!我应该给你 我收到过的最佳评论 徽章。 :))) - Ivan Kharlamov
我不知道,也许这并不是写这个的最好地方,但是你给我展现出了一个非常成熟的形象,重视周围的人,并且知道如何关心他们。你让我想起了我的祖父。 - Ivan Kharlamov
谢谢您的夸奖,但您过于高估了我!我对您也很好奇,因为您十分慷慨地帮助一个完全陌生人解决问题: )。这样的人让世界变得更美好,我相信!非常抱歉昨天没有回复您,但我意外地不得不整天工作。今天是挪威的假日(我想其他许多国家也是),所以意味着 Python,欢呼:) 您的答案解决了我的问题,所以再次感谢您!! - Rookie
2
嗨,昨天我们也放假了,但我和父亲喝伏特加并谈论我们的亲戚在战争中战斗或死亡的经历。实际上,过去我深度参与非商业项目,并且未来也有一些计划。这里是我帮助推广的作家一些作品。再次感谢你的好话!))))) - Ivan Kharlamov

2
当你说你设置了AUTH_USER_MODEL = 'app.MyUser'时,我认为你的应用程序中有一个MyUser类,它位于结构如下的目录中:

在app/目录下:init.py和models.py等文件。

所以,在models.py中,你有MyUser,在init.py中:

from models import MyUser


感谢您抽出时间查看我的问题@user1808134。是的,该应用程序包含__init__.py文件。我之前没有在其中声明任何内容,但在看到您的答案后现在已经这样做了。我插入了__all__ = ['views','tests', 'static','models','admin',],但无济于事,在管理员页面上按“登录”时仍会出现相同的错误(PS.也尝试在__init__.py文件中使用from models import MyUser)。 - Rookie
我尝试将AUTH_USER_MODEL设置为mainfolder.app.MyUser,我确定"mainfolder"在pythonpath上。 我仍然有如上所述的__init__.py。 新的settings.py给出了以下服务器错误; auth.user: AUTH_USER_MODEL不符合'app_label.app_name'的格式。admin.logentry: 'user'与模型smartflightsearch.SFSdb.MyUser有关联,该模型未安装或为抽象。registration.registrationprofile: 'user'与模型有关联,该模型未安装或为抽象。这能提供任何提示吗? - Rookie
我对这个问题进行了一些研究,但没有找到什么有启发性的东西 =/上次我使用新的Django自定义认证进行工作时,我遵循了Django文档中的说明,我知道这并不多。 - Douglas Miranda
谢谢您的时间。顺便提一下,我得到了上面回答的帮助。 - Rookie

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