Create user inactive as default (is_active default False)
Asked Answered
P

6

6

I have facebook authentication in my website which I use omab / django-social-auth

I want to redirect users to another website to make them fill their details. So I want to have users as inactive when they first authenticate with their facebook accounts, then after they complete the form I save them as active users.

I manipulated the django/contrib/auth/models.py under my enviroment as with is_active fields as default=False; but they are saved as active user but still the same result, even I add a normal user from the admin panel. Is there something I am missing?

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
Paleozoic answered 27/2, 2013 at 21:35 Comment(0)
E
17
  1. Avoid modifying built-ins. There are better ways to do things.
  2. Like signals. Signals are awesome.
  3. In this case, I'd attach to the pre_save signal of django.contrib.auth.models.User, and manually correct the is_active property of the model instance (if the object is new).
  4. This way, you can add some logic to make sure you're properly marking a user as not active.
  5. Because user's added within the Admin should probably be active, if the admin marks them as active.
Electroacoustics answered 27/2, 2013 at 21:38 Comment(4)
That's cool.. So I have to manipulate the django-social-auth package to add a signal...right?Paleozoic
Not really. You can do that from any application within your Django project. One should avoid patching packages if one can.Electroacoustics
How to check if the user is being created and not just being modified?Marchland
"I'd attach to the pre_save signal of django.contrib.auth.models.User" but using what code?Macmullin
H
12

jack_shed suggested signals, which helped me find the direction to take it. But there was still work from there to figure out how exactly to test and modify after receiving the signal.

Here's what worked for me.

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")

This will catch the action of creating a user before the save occurs, then test if this instance state is "adding" or not. That differentiates between creating and updating a model instance.

If you don't do this test, updating the user sets is_active to False also, and there ends up being no way to activate them through django.

Happen answered 26/1, 2017 at 18:17 Comment(2)
I'm not trying to be difficult, or picky, I genuinely want to know... isn't it considered bad practice to reference a "private" property? In this case, _state. I know the "_" is merely a convention in python, not a truly private property, but I thought the underlying rule was that any "private" property (or method) could change in subsequent updates, so best not rely on them.Navarrete
I can't claim to be avoiding a bad practice here. I needed the functionality, and this is the method that worked for me. Quite possibly this check on the _state property will at some point fail after an update. Ideally, I'd find another way. But this has been in production for years now and better to not mess with it I think. Thank you for point it out. That will make it more likely I'll find the failure point if it happens. :-)Happen
P
9

Elegant solution when using django-allauth.

There is another very nice solution.. one that sounds very much like what you desire.

I have created a custom form (in my case a ModelForm) that I can hand over to django-allauth via the ACCOUNT_SIGNUP_FORM_CLASS setting. What this does.. is ask the new potential user to supply additional fields during the signup process.

That has some very nice advantages:

  1. You can add some fields very elegantly in addition to the default stuff.
  2. It works for both social and "normal" signup.
  3. No patching of 3rd party apps required.
  4. You are still able to modify and maintain everything in the admin.
  5. In the custom form you get access to the new user instance before it gets saved to the database. This means you can even process the provided information do things like create a profile object for him and set the user as inactive ..all in one go. This works because you can check if everything is ok.. and only then commit to do all these steps or reject the form with a validation error. :)

Well.. sounds good right?
But how exactly does it work (i.e. look like)?
I am glad you asked.. ^_^

For your use case it might look something like this:

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()
Padraig answered 11/9, 2014 at 22:6 Comment(1)
@Afshin at point 5 > "In the custom form you get access to the new user instance before it gets saved to the database"- did you mean to use signals here ?Alcinia
T
0

I think a better approach would be to customize your own model manager by leaving the user value inactive as default.

Create in your app: 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)

and in you models:

#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. 

       

Thereupon answered 19/10, 2022 at 11:33 Comment(0)
E
0

In general, for email validation, Django has its own table. You can check e.g. mysql> Select * From account_emailconfirmation; So can you not just check if the email is in this table?

BTW, if emails are not sent to your mailbox: I struggled a lot and found out that this configuration was missing: DEFAULT_FROM_EMAIL = '[email protected]'. Otherwise, its webmaster@... which was not working for me with mod_wsgi.

Epilepsy answered 26/8, 2023 at 0:27 Comment(0)
R
0

Using django-allauth's ACCOUNT_ADAPTER

If you're using django-allauth, then you can modify ACCOUNT_ADAPTER in your settings.py to your custom class, like this:

ACCOUNT_ADAPTER = 'foobar.adapter.AccountAdapter'

Create a file in the directory foobar/ named adapter.py (alongside __init__.py), and add this code, subclassing DefaultAccountAdapter:

from allauth.account.adapter import DefaultAccountAdapter
from django.contrib.auth import get_user_model

class AccountAdapter(DefaultAccountAdapter):
    def new_user(self, request):
        """
        Instantiates a new User instance.
        """
        User = get_user_model()
        user = User(is_active=False)
        return user
Ranzini answered 19/6, 2024 at 14:23 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.