I have a simple CBV for a signup form.
form_valid is overridden so that a user can be created with a ‘try’ and ‘except’ to catch any exception should a user already exist.
I have deliberately not used form validation to check for an existing user, because I want to make additional checks, which you can see in the code below.
However, when I try to register and existing user, I get the following error:
raise TransactionManagementError(
django.db.transaction.TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.
What am I doing wrong?
View
class SignUpView(FormView):
template_name = 'accounts/signup.html'
form_class = SignUpForm
success_url = reverse_lazy('accounts:signup_verify')
def form_valid(self, form):
try:
self.user = CustomUser.objects.create_user(email=form.cleaned_data['email'],
full_name=form.cleaned_data['full_name'],
password=form.cleaned_data['password'],
is_verified=False
)
self.send_code()
except IntegrityError:
user = CustomUser.objects.filter(email=form.cleaned_data['email']).first()
if user and user.is_verified:
# If user already exists and is verified then send a reminder.
self.send_reminder(self, form)
else:
# Otherwise send a code
self.send_code()
return super().form_valid(form)
Hey there!
It seems that you’re running this code inside a transaction. Altough this is not explicit in your code. So i assume that you’re using ATOMIC_REQUESTS.
Please consider reviewing the note under Avoid catching exceptions inside atomic! that is on this section of the documentation.
After that, you may consider doing the check before trying to create a user.
The idea of catching a exception is to handle a specific error case, but on your use case it seems that you already know that this exception could be avoided, that’s when the user already exists, and how to handle this case, so there’s no need to do this try/except block, instead you should be checking if the user already exists and do something about it.
You are absolutely correct, I am using ATOMIC_REQUESTS.
I was trying to use try/except in an unconventional way, which is obviously not the right way.
I have amended my code as you have suggested:
class SignUpView(FormView):
template_name = 'accounts/signup.html'
form_class = SignUpForm
success_url = reverse_lazy('accounts:signup_verify')
def form_valid(self, form):
if not CustomUser.objects.filter(email=form.cleaned_data['email']).exists():
try:
self.user = CustomUser.objects.create_user(email=form.cleaned_data['email'],
full_name=form.cleaned_data['full_name'],
password=form.cleaned_data['password'],
is_verified=False
)
# todo log new user created email, full_name, password
self.user_verify, created = CodeVerify.objects.update_or_create(email=self.user.email,
defaults={'code': generate_code()}
)
self.request.session['signup_data'] = (self.user_verify.pk, self.user.email)
self.send_code()
except:
messages.error(self.request, _('Something went wrong, please try to register again'))
return redirect(reverse('accounts:signup'))
else:
user = CustomUser.objects.filter(email=form.cleaned_data['email']).first()
if user:
if user.is_verified:
# If user already exists then send a reminder.
self.send_reminder(form.cleaned_data['email'])
else:
# Otherwise send a code
self.send_code()
else:
messages.error(self.request, _('Something went wrong, please try to register again'))
return redirect(reverse('accounts:signup'))
return super().form_valid(form)
class SignUpView(FormView):
template_name = 'accounts/signup.html'
form_class = SignUpForm
success_url = reverse_lazy('accounts:signup_verify')
def form_valid(self, form):
user = CustomUser.objects.filter(email=form.cleaned_data['email']).first()
if user is None:
try:
self.user = CustomUser.objects.create_user(email=form.cleaned_data['email'],
full_name=form.cleaned_data['full_name'],
password=form.cleaned_data['password'],
is_verified=False
)
# todo log new user created email, full_name, password
self.user_verify, created = CodeVerify.objects.update_or_create(email=self.user.email,
defaults={'code': generate_code()}
)
self.request.session['signup_data'] = (self.user_verify.pk, self.user.email)
self.send_code()
except:
messages.error(self.request, _('Something went wrong, please try to register again'))
return redirect(reverse('accounts:signup'))
else:
if user.is_verified:
# If user already exists then send a reminder.
self.send_reminder(form.cleaned_data['email'])
else:
# Otherwise send a code
self.send_code()
else:
messages.error(self.request, _('Something went wrong, please try to register again'))
return redirect(reverse('accounts:signup'))
return super().form_valid(form)