customSignupForm code to allow signup for only the invited user I am developing a feature where the user has to request for invitation if he/she wants to sign up on the site. When the request is accepted by the admin, the user gets to sign up, otherwise it shows them an error saying they are not invited at this time, and saves a email so we can follow up with him/her. To be generic, I used the condition INVITE_MODE = True in settings, so if we disable the invitation feature using INVITE_MODE=False, all the users should get to sign up.

Here is my signup form and I am using Django allauth for that:

class CustomSignupForm(forms.Form):
    def __init__(self, *args, **kwargs):
        super(CustomSignupForm, self).__init__(*args, **kwargs)

    def clean(self):
        email = self.cleaned_data.get('email')
        print ('email', email)
        email = get_adapter().clean_email(email)
        # if email and app_settings.UNIQUE_EMAIL:
        #     email = self.validate_unique_email(email)
        if email and settings.INVITE_MODE:
            try:
                Invitation.objects.get(email=email, request_approved=True)
            except Invitation.DoesNotExist:
                invitation, created = Invitation.objects.get_or_create(email=email)
                self.add_error('email', 'Sorry at this time you are not invited. But we have added you to our invite list')
        elif email is None:
            self.add_error('email', 'Email field cannot be empty')
        else:
            return email


    def signup(self, request, user):
        # email = self.cleaned_data.get('email')
        # user.email = email
        user.save()


Can anyone suggest a to do this in a better/efficient way?
A good motivation to overwrite the Form's clean() method is when you need to modify/check multiple fields (reference):


  The form subclass’s clean() method can perform validation that
  requires access to multiple form fields. This is where you might put
  in checks such as “if field A is supplied, field B must contain a
  valid email address”.


And, the overwritten clean() method usually returns the modified cleaned_data.

In your case, you only work with a single field, use the clean_&lt;fieldname&gt;() method - clean_email() to be specific.

I would also handle the "email is falsy" case beforehand (though, I am not completely sure if you actually need to worry about it here and having required=True on your email form field is not enough).

Also, I'm not sure you actually need to call clean_email() on the adapter - if you are using the default account adapter, I think it is doing nothing and is simply a "stub" for custom adapters (source).

I think using get_or_create() first and then checking the request_approved value could be a bit cleaner.

Here is a variation of the code with applied assumptions and suggestions (described above):

def clean_email(self):
    email = self.cleaned_data.get['email']

    if settings.INVITE_MODE:
        invitation, _ = Invitation.objects.get_or_create(email=email)
        if not invitation.request_approved:
            self.add_error('email', 'Sorry at this time you are not invited. But we have added you to our invite list')

    return email             


One concern with that though - I am not sure if creating an invitation record from a form field's clean form method is a good thing and is not a sign of a bad design..

Another note is that if a new user would submit the form multiple times with the same email address, the user would get the same "Sorry at this time you are not invited. But we have added you to our invite list" message. Instead, may be, if email does not exist, you can say that the user was added to the invite list, but if email exists but the invitation is not approved, you can say something more specific, like "Your invitation is waiting for approval".