将用户默认创建为未激活状态(is_active默认为False)

6
我在我的网站上使用omab / django-social-auth进行Facebook身份验证。
我想将用户重定向到另一个网站以填写其详细信息。因此,当他们首次使用其Facebook帐户进行身份验证时,我希望将用户设置为非活动状态,然后在他们完成表单后将其保存为活动用户。
我在我的环境下操纵django/contrib/auth/models.py,默认情况下is_active字段为False;但是它们被保存为活动用户,即使我从管理面板添加了一个普通用户也是如此。我是否遗漏了什么?
class User(models.Model):
    """
    Users within the Django authentication system are represented by this
    model.

    Username and password are required. Other fields are optional.
    """
    username = models.CharField(_('username'), max_length=30, unique=True,
        help_text=_('Required. 30 characters or fewer. Letters, numbers and '
                    '@/./+/-/_ characters'))
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
    email = models.EmailField(_('e-mail address'), blank=True)
    password = models.CharField(_('password'), max_length=128)
    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=False,
        help_text=_('Designates whether this user should be treated as '
                    'active. Unselect this instead of deleting accounts.'))
    is_superuser = models.BooleanField(_('superuser status'), default=False,
        help_text=_('Designates that this user has all permissions without '
                    'explicitly assigning them.'))
    last_login = models.DateTimeField(_('last login'), default=timezone.now)
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
    groups = models.ManyToManyField(Group, verbose_name=_('groups'),
        blank=True, help_text=_('The groups this user belongs to. A user will '
                                'get all permissions granted to each of '
                                'his/her group.'))
    user_permissions = models.ManyToManyField(Permission,
        verbose_name=_('user permissions'), blank=True,
        help_text='Specific permissions for this user.')
    objects = UserManager()


 def create_user(self, username, email=None, password=None):
    """
    Creates and saves a User with the given username, email and password.
    """
    now = timezone.now()
    if not username:
        raise ValueError('The given username must be set')
    email = UserManager.normalize_email(email)
    user = self.model(username=username, email=email,
                      is_staff=False, is_active=False, is_superuser=False,
                      last_login=now, date_joined=now)

    user.set_password(password)
    user.save(using=self._db)
    return user
5个回答

17
1. 避免修改内置函数。有更好的方法来完成任务。 2. 像 signals 这样的信号是很棒的。 3. 在这种情况下,我会附加到 django.contrib.auth.models.User 的 pre_save 信号,并手动更正模型实例的 is_active 属性(如果对象是新的)。 4. 这样,您可以添加一些逻辑以确保正确地将用户标记为非活动状态。 5. 因为在管理员中添加的用户应该是活动的,如果管理员将他们标记为活动的话。

很酷啊。所以我需要操纵django-social-auth包来添加一个信号,是吗? - tuna
1
不是真的。您可以从Django项目中的任何应用程序中执行此操作。如果可以的话,应该避免打补丁包。 - Jack Shedd
1
如何检查用户是否正在被创建而不仅仅是被修改? - Sidhin S Thomas
但是要使用什么代码?我会附加到django.contrib.auth.models.User的pre_save信号。 - Mike 'Pomax' Kamermans

12

jack_shed提出了一些信号,帮助我找到了该采取的方向。但在收到信号后,仍需进行工作以确定如何进行测试和修改。

以下是对我有效的方法。

from django.dispatch import receiver
from django.db.models.signals import pre_save
from django.contrib.auth.models import User

@receiver(pre_save, sender=User)
def set_new_user_inactive(sender, instance, **kwargs):
    if instance._state.adding is True:
        print("Creating Inactive User")
        instance.is_active = False
    else:
        print("Updating User Record")

在保存操作之前,这将捕获创建用户的行为,然后测试此实例状态是否为“添加”。这区分了模型实例的创建和更新。

如果您不进行此测试,则更新用户也会将"is_active"设置为False,并且最终没有办法通过Django激活它们。


我并不是想刁难或挑剔,我真的想知道... 引用“私有”属性是否被认为是不良实践?在这种情况下,是 _state。我知道 "_" 在 Python 中仅仅是一种约定,而不是真正的私有属性,但我认为底层规则是任何“私有”属性(或方法)都可能在后续更新中更改,因此最好不要依赖它们。 - ExTexan
1
我不能声称在这里避免了一种不好的做法。我需要这个功能,而这是适合我的方法。很可能,在更新后,对_state属性的检查会在某个时候失败。理想情况下,我会找到另一种方法。但是现在它已经在生产中使用了多年,最好不要去碰它。谢谢你指出来。如果发生故障,这将使我更有可能找到故障点。 :-) - shanemgrey

8

使用django-allauth时的优雅解决方案。

这里有另一个非常好的解决方案...听起来非常像你所需要的。

我创建了一个自定义表单(在我的情况下是ModelForm),我可以通过ACCOUNT_SIGNUP_FORM_CLASS设置将其交给django-allauth。这样做会要求新潜在用户在注册过程中提供其他字段。

它有一些非常好的优点:

  1. 您可以非常优雅地添加一些字段以补充默认内容。
  2. 它适用于社交和“正常”的注册。
  3. 不需要修补第三方应用程序。
  4. 您仍然能够在管理界面中修改和维护所有内容。
  5. 在自定义表单中,您可以在保存到数据库之前访问新用户实例。这意味着您甚至可以处理提供的信息并为其创建配置文件对象,并将用户设置为非活动状态...这一切都可以一次性完成。这是因为您可以检查是否一切正常..只有在确认所有这些步骤或拒绝带有验证错误的表单后才提交。

好的..听起来不错吧?
但是它到底是如何工作的(也就是长什么样)?
我很高兴你问了.. ^_^

对于您的用例,它可能看起来像这样:

settings.py

[...]
ACCOUNT_SIGNUP_FORM_CLASS = "<your_app>.forms.SignupForm"
[...]

forms.py

class SignupForm(forms.Form):
    first_name = forms.CharField(max_length=30)
    last_name = forms.CharField(max_length=30)

    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.is_active = False
        user.save()

@Afshin 在第5点提到:“在自定义表单中,您可以在将新用户实例保存到数据库之前访问它”- 您是想在这里使用信号吗? - kayesh parvez

0
一般来说,对于电子邮件验证,Django有自己的表格。你可以检查例如mysql> Select * From account_emailconfirmation; 来确认邮箱是否在这个表格中。
顺便说一下,如果邮件没有发送到你的邮箱:我曾经遇到了很多困难,后来发现缺少了这个配置项:DEFAULT_FROM_EMAIL = 'test.noreply@gmail.com'。否则,默认是webmaster@...,但在我的mod_wsgi环境下无法正常工作。

0

我认为更好的方法是通过将用户值默认设置为非活动状态来自定义自己的模型管理器。

在您的应用程序中创建:project_app/managers.py:

# project_app/managers.py 

from django.contrib.auth.base_user import  BaseUserManager

class UserManager(BaseUserManager):

        def create_user(self, username, email=None, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        extra_fields.setdefault('is_active', False)
        return self._create_user(username, email, password, **extra_fields)

并在你的模型中:

#project_app/models.py 
from .managers import UserManager
from django.contrib.auth.models import AbstractUser

class UserManager(AbstractUser):
    objects = UserManager()
    # your custom fields go here. 

       


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