无法使用自定义用户和 Mezzanine/South 登录到 Django Admin

Posted

技术标签:

【中文标题】无法使用自定义用户和 Mezzanine/South 登录到 Django Admin【英文标题】:Can't login to Django Admin with custom user and Mezzanine/South 【发布时间】:2014-12-13 15:39:12 【问题描述】:

我有一个像这样的自定义用户的 django 应用程序:

import ...

logger = logging.getLogger(__name__)


# # Person (representation of people)


class AbstractPerson(models.Model):
    """
    ABSTRACT model representing basic information about a person on the platform,
    be it a user or a person used in acts he's generating.
    """

    MALE = 'M'
    FEMALE = 'F'
    GIRL = 'G'
    FRENCH_TITLES = (
        (MALE, 'M.'),
        (FEMALE, 'Mme'),
        (GIRL, 'Mlle'),
    )

    date_created = models.DateTimeField(_('date created'),
                                        default=timezone.now,
                                        db_index=True)

    title = models.CharField(u'Civilité', max_length=1,
                             choices=FRENCH_TITLES,
                             blank=True)
    first_name = models.CharField(u'Prénom', max_length=500, blank=True)
    last_name = models.CharField(u'Nom', max_length=500, blank=True,
                                 db_index=True)
    phone = models.CharField(u'Téléphone', max_length=10, blank=True)
    address_1 = models.CharField(u'Adresse', max_length=200, blank=True)
    address_2 = models.CharField(u"Complément d'adresse", max_length=200,
                                 blank=True, null=True)
    zipcode = models.CharField(u'Code Postal', max_length=10, blank=True)
    city = models.CharField(u'Ville', max_length=50, blank=True)


    class Meta:
        abstract = True


# # Auth

class UserManager(BaseUserManager):
    """Replacement for django auth's UserManager"""

    def create_user(self, email, password=None, **extra_fields):
        """
        Creates and saves a User with the given email and password.
        """
        if not email:
            raise ValueError('Users must have an email address')
        email = UserManager.normalize_email(email)

        now = timezone.now()

        user = self.model(email=email,
                          is_staff=False, is_active=True, is_superuser=False,
                          last_login=now, date_created=now, **extra_fields)

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

    def create_superuser(self, email, password, **extra_fields):
        u = self.create_user(email, password=password, **extra_fields)
        u.is_staff = True
        u.is_active = True
        u.is_superuser = True
        u.save(using=self._db)
        return u


class User(AbstractPerson, AbstractBaseUser, PermissionsMixin):
    """
    Replacement for django.contrib.auth's User.
    Gets basic Person information injected from AbstractPerson
    """

    # Regular fields
    email = models.EmailField(_('email address'), max_length=255,
                              unique=True, db_index=True)
    is_staff = models.BooleanField(_('staff status'), default=False,
                                   help_text=_('Designates whether the user '
                                               'can log into this admin site.'))
    is_active = models.BooleanField(_('active'), default=True,
                                    help_text=_('Designates whether the user '
                                                'should be treated as active. '
                                                'Unselect this instead of '
                                                'deleting accounts.'))

    # Compatibility with Mezzanine accounts app
    username = models.EmailField(_('username (copy of email address)'),
                                 max_length=255,
                                 unique=True, db_index=True)

    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []  # Nothing required apart from email


    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
    abstract = False

    def save(self, *args, **kwargs):
        self.username = self.email
        try: kwargs.pop('username')
        except KeyError: pass
        super(User, self).save(*args, **kwargs)

那么我的设置是:

# Custom user model
AUTH_USER_MODEL = 'useraccount.User'

INSTALLED_APPS = (
    'grappelli',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.comments',
    'django.contrib.contenttypes',
    "django.contrib.redirects",
    'django.contrib.sessions',
    "django.contrib.sites",
    "django.contrib.sitemaps",
    'django.contrib.staticfiles',
    "storages",          # django-storages backend
    ## Custom user model and such
    'useraccount',
    ## Mezzanine
    "mezzanine.boot",
    "mezzanine.conf",
    "mezzanine.core",
    "mezzanine.generic",
    #"mezzanine.blog",
    "mezzanine.forms",
    "mezzanine.pages",
    #"mezzanine.galleries",
    #"mezzanine.twitter",
    #"mezzanine.accounts",
    #"mezzanine.mobile",
    'south',
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    "mezzanine.core.request.CurrentRequestMiddleware",
    "mezzanine.core.middleware.RedirectFallbackMiddleware",
    "mezzanine.core.middleware.TemplateForDeviceMiddleware",
    "mezzanine.core.middleware.TemplateForHostMiddleware",
    "mezzanine.core.middleware.AdminLoginInterfaceSelectorMiddleware",
    "mezzanine.core.middleware.SitePermissionMiddleware",
    # Uncomment the following if using any of the SSL settings:
    #"mezzanine.core.middleware.SSLRedirectMiddleware",
    "mezzanine.pages.middleware.PageMiddleware",
    "mezzanine.core.middleware.FetchFromCacheMiddleware",
)

现在,我已经同步了数据库并创建了一个超级用户。在 shell 中,一切看起来都不错:

In [1]: from useraccount.models import User

In [2]: from django.contrib.auth import authenticate

In [3]: authenticate(username='john', password='secret')
Out[3]: <User: john>

In [4]: u = User.objects.all()[0]

In [5]: u.is_active
Out[5]: True

In [6]: u.is_staff
Out[6]: True

In [7]: u.is_superuser
Out[7]: True

尽管如此,无法连接到管理员...管理员没有说: Please enter the correct email address and password for a staff account. Note that both fields may be case-sensitive.

但是: Please correct the error below.

有什么想法吗?

编辑

对不起,我没有说清楚我使用 MezzanineBackend 来验证用户:

AUTHENTICATION_BACKENDS = ("mezzanine.core.auth_backends.MezzanineBackend",)

代码是这样的:

class MezzanineBackend(ModelBackend):

def authenticate(self, **kwargs):
    if kwargs:
        username = kwargs.pop("username", None)
        if username:
            username_or_email = Q(username=username) | Q(email=username)
            password = kwargs.pop("password", None)
            try:
                user = User.objects.get(username_or_email, **kwargs)
            except User.DoesNotExist:
                pass
            else:
                if user.check_password(password):
                    return user
        else:
            if 'uidb36' not in kwargs:
                return
            kwargs["id"] = base36_to_int(kwargs.pop("uidb36"))
            token = kwargs.pop("token")
            try:
                user = User.objects.get(**kwargs)
            except User.DoesNotExist:
                pass
            else:
                if default_token_generator.check_token(user, token):
                    return user

可能问题是没有get_user方法,所以我试试这个,如果不行就回来

EDIT2

添加的 get_user 方法没有任何变化...有什么想法吗?

【问题讨论】:

【参考方案1】:

我认为您的问题来自这样一个事实,即您的新用户模型虽然注册到 settings.py 没有关联的身份验证后端。因此,他无法在您的应用中进行身份验证。

按照本教程:https://docs.djangoproject.com/en/dev/topics/auth/customizing/ 你应该有一个看起来像这样的文件:

from django.conf import settings
from django.contrib.auth.models import User, check_password

class SettingsBackend(object):
"""
Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.

Use the login name, and a hash of the password. For example:

ADMIN_LOGIN = 'admin'
ADMIN_PASSWORD = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de'
"""

def authenticate(self, username=None, password=None):
    login_valid = (settings.ADMIN_LOGIN == username)
    pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
    if login_valid and pwd_valid:
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            # Create a new user. Note that we can set password
            # to anything, because it won't be checked; the password
            # from settings.py will.
            user = User(username=username, password='get from settings.py')
            user.is_staff = True
            user.is_superuser = True
            user.save()
        return user
    return None

def get_user(self, user_id):
    try:
        return User.objects.get(pk=user_id)
    except User.DoesNotExist:
        return None

settings.py这样注册:

AUTHENTICATION_BACKENDS ('mybackend',)

您可能还需要一个覆盖django.contrib.auth.admin.UserAdminadmin.py 文件 然后就可以注册了

 admin.site.register(get_user_model(), YourUserAdmin)

为了能够在管理控制台中看到它。

PS is_staff 和 is_superuser 应该是可调用的,而不是布尔值

【讨论】:

【参考方案2】:

好吧,是 django-grappelli 搞砸了我的管理员。我将其更改为 grappelli_safe,现在一切都很好:)

【讨论】:

以上是关于无法使用自定义用户和 Mezzanine/South 登录到 Django Admin的主要内容,如果未能解决你的问题,请参考以下文章

无法使用带有自定义 UserDetail 和 AuthenticationProvider 的 Spring Security 来阻止用户多次登录

Django 管理员无法使用自定义用户模型正确登录

无法使用弹簧自定义过滤器对用户进行身份验证

无法在 Django 1.5 中使用自定义用户模型创建超级用户

在 WPF C# 中无法访问用户控件的自定义属性

django-rest-auth 自定义注册无法保存额外字段