The feature in my app is a user should first request for invitation and if his invitation is accepted by admin(from admin), a message should be sent that you can signup.
Flow of the feature
User submits the form with his/her email address and mail will be sent saying thank you. Now if admin accepts his/her request, i.e request_accepted will be True now and a mail will be sent saying now you can signup. Finally when user go to signup page and submit the form, it should check if the email that he has filled and submitted during signup has been accepted or not. If accepted, signup is complete with success otherwise display an error stating this email address has not been invited.
class Invitation(models.Model):
email = models.EmailField(unique=True, verbose_name=_("e-mail Address"))
request_approved = models.BooleanField(default=False, verbose_name=_('request accepted'))
@receiver(post_save, sender=Invitation)
def send_email_when_invite_is_accepted_by_admin(sender, instance, *args, **kwargs):
request_approved = instance.request_approved
if request_approved:
subject = "Request Approved"
message = "Hello {0}! Your request has been approved. You can now signup".format(instance.email)
from_email = None
to_email = [instance.email]
send_mail(subject, message, from_email, to_email, fail_silently=True)
def requestInvitation(request):
form = InviteForm(request.POST or None)
if form.is_valid():
join = form.save(commit=False)
email = form.cleaned_data.get('email')
already_join, created = Invitation.objects.get_or_create(email=email)
if created:
already_join.save()
subject = "Thank you for your request to sign up our community"
message = "Welcome! We will be in contact with you."
from_email = None
to_email = [email]
send_mail(subject, message, from_email, to_email, fail_silently=True)
messages.success(request, '{0} has been invited'.format(email))
return HttpResponseRedirect("/")
context = {"form": form}
return render(request, 'home.html', context)
ACCOUNT_SIGNUP_FORM_CLASS = 'pages.custom_sign_up_form.CustomSignupForm'
custom_signup_form.py
from invitation.models import Invitation
class CustomSignupForm(forms.Form):
def __init__(self, *args, **kwargs):
super(CustomSignupForm, self).__init__(*args, **kwargs)
def clean_email(self):
value = self.cleaned_data.get('email')
print ('value before adapter', value)
value = get_adapter().clean_email(value)
print ('value', value)
if value and app_settings.UNIQUE_EMAIL:
value = self.validate_unique_email(value)
try:
Invitation.objects.get(email=value, request_approved=True)
except Invitation.DoesNotExist:
raise forms.ValidationError(errors['Sorry! you are not yet invited'])
return value
def signup(self, request, user):
user.save()
The way i am doing allows to signup to non accepted user too.
UPDATE
if i put clean_email logic inside signup as
def signup(self, request, user):
print ('request', request, user)
value = self.cleaned_data.get('email')
print ('email is', value)
try:
Invitation.objects.get(email=value, request_approved=True)
except:
raise forms.ValidationError(errors['sorry! You are not yet invited'])
user.save()
i get 'name errors is not defined'
raise forms.ValidationError(errors['sorry! You are not yet invited'])
There is going quite wrong here. You are saying you have a dict with sorry! You are...
as a key
, and expecting it to return a value.
This is what you are attempting to access:
errors = {
'sorry! you are not yet invited': None,
}
A key like that, with exclamation marks, spaces, etc is invalid.
Note that error
is not defined in the method (and not anywhere I can see in the class). If you have initialized as a class
variable, you should access it with self
.
If you have not yet declared the error dict with a default error message, you should create it as a class variable.
class CustomerSignupForm(forms.Form):
errors = {
'not_invited': "Sorry! You are not yet invited.",
}
Now in your signup method, you can access the error message:
raise forms.ValidationError(
self.errors['not_invited'],
code='not_invited'
)
I'm not 100% sure right now what the problem is. But there are a few things you can take a look at and improve.
Point 1: Your variable names should explain clearly what it contains. I.e.
value = self.cleaned_data.get('email')
is a field containing an e-mail string or None. Keep it simple and clear, and keep email
as variable name. If the value changes in your logic, you can rename it to a new name that clearly explains the contents of the variable. value
is a too broad name, and is quite confusing.
value = get_adapter().clean_email(value)
What is happening here? You have a print after defining value (bad name again), what does it return? What do you expect it to do?
Point 2: Keep your validation in the clean
methods
Your try
and except
with the ValidationError
should be in your form cleaning method. If it does not properly fit in one of the fields that are being cleaned, you should override the clean
method, run the super
's clean method and add the custom method that validates what you want.
Point 3: The signup
method.
I've not seen a place where you call the signup
method. It is not inherited from forms.Form
, so it is not yet called as far as I can see.
Note: I'm not going to create any new answers to this topic, I will be updating this 'answer'.