0

My aim - Trying to create an authentication system in Django, and allows user to signup again with same username if their account is not activated.

If an user try to register with a certain username and that username already exists then update that user with this current user.

My Approach - I have created a form using "UserCreationForm" and defining my own methods to validate the form, and allows the user even if username already exists but user.is_active = False.

Code

forms.py

from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import User

class SignupForm(UserCreationForm):
    email = forms.EmailField(max_length=200, help_text='Required')
    name = forms.CharField()
    institution = forms.CharField()

    def clean_username(self):
        username = self.cleaned_data.get('username')
        user = None
        try:
            try:
                user = User.objects.get(username=username)
                print("is user active username", user.is_active)
            except ObjectDoesNotExist as e:
                pass
            except Exception as e:
                raise e
            if not user:
                pass
            elif not user.is_active:
                pass
            else:
                raise forms.ValidationError("This Username Already Exists")
        except Exception as e:
            raise e
        return username

     class Meta:
        model = User
        fields = ('username', 'email', 'institution', 'password1', 'password2')

views.py

from .forms import SignupForm
def regUser(form):
    '''
    It will save Django user and student.
    It recieves form.cleaned_data as argument
    '''
    print("reg user line 1")
    user = User.objects.create_user(username=form['username'],
                                     email=form['email'], 
                                     password=form['password1'],
                                     is_active=False,
                                    )
    # notice is_active = False, becuase we will activate account if user will
    # verify the email
    user.save()
    student = Student(user=user, name=form['name'], institution=form['institution'])
    student.save()
    return user

def signup(request):
    if request.user.is_authenticated:
        return redirect('accounts:home')
    if request.method == 'POST':
        form = SignupForm(request.POST)
        if form.is_valid():
            user = regUser(form.cleaned_data)
            current_site = get_current_site(request)
            message = render_to_string('accounts/acc_active_email.html', {
                'user':user, 'domain':current_site.domain,
                'uid': urlsafe_base64_encode(force_bytes(user.pk)),
                'token': account_activation_token.make_token(user),
            })

            mail_subject = 'Activate your account.'
            to_email = form.cleaned_data.get('email')
            email = EmailMessage(mail_subject, message, to=[to_email])
            email.send()
            return render(request, 'accounts/signup.html', {'email_sent': True})
        else:
            for field in form:
                for error in field.errors:
                    messages.error(request, error)
            return redirect("accounts:signup")

    return render(request, 'accounts/signup.html')

models.py

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    subject = models.CharField(max_length=250, default="default_value")


class Student(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
    name = models.CharField(max_length=250, default="default_value")
    institution = models.CharField(max_length=250, default="default_value")

    def save(self):
        super().save()

    def __str__(self):
        return self.user.username

My problem - I am getting error " A user with that username already exists" I don't why and from where this error is coming when I am trying to created a new user with already registered username but it account is not activated yet (user.is_active = False).

As much I know clean_fields functions are called to validate the form when we use "form.is_valid()" in our views.py, But I have already overridden the "clean_username" according to my need so why I am getting this error ? and from where it is generated ? and how to solve this ?

Also I would like to mention when user is registered it is also registered "student" that's why I am using "regUser" function. Also I read some other questions similar to this on stackoverflow this and this some answer was that inherit the form from "forms.Form" not from "UserCreationForm" but why can anyone explain?

Please not mark this question as already answered I have gone through all questions and it was not helpful.

Abhishek Pratap Singh
  • 613
  • 3
  • 11
  • 18

1 Answers1

1

The User model you defined inherited the AbstactUser, and username is defined there.

class AbstractUser(AbstractBaseUser, PermissionsMixin):
    """
    An abstract base class implementing a fully featured User model with
    admin-compliant permissions.

    Username and password are required. Other fields are optional.
    """
    username_validator = UnicodeUsernameValidator()

    username = models.CharField(
        _('username'),
        max_length=150,
        unique=True,
        help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
        validators=[username_validator],
        error_messages={
            'unique': _("A user with that username already exists."),
        },
    )
    ...

The attribute unique for username field means that username must be unique among all users, not only active users. (ref. https://docs.djangoproject.com/en/3.1/ref/models/fields/#unique)

If what you want is to allow users to have the same username among inactive users, set unique=False.

But, if 'inactive' means 'discarded' in your User model, I would recommend changing user's username to garbage value when the user is being inactivated.

Youngkwang Kim
  • 168
  • 1
  • 1
  • 6