I am checking a user's submitted email address against two lists from the database -- a list of authorized domains and a list of authorized email addresses. Currently, if neither are found, it is rainsing a DoesNotExist exception. How would I handle this if neither are found?
Here is the code I currently have in views.py --
def register(request):
if request.method == 'POST':
form = UserForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
try:
email_list = EmailList.objects.get(domain=(cd['email'].split('#')[1]))
except:
email_list = EmailList.objects.get(email=cd['email'])
# I also need another except if neither works for the validator.
network= Network.objects.get(network=email_list.network)
User.objects.create(name=cd['name'], email=cd['email'], network=network)
return HttpResponseRedirect ('/user/view/')
else:
form = UserForm()
return render_to_response('register.html',{'form':form}, context_instance = RequestContext(request))
Nested try/except blocks. And don't use a bare except; catch only the exceptions you can handle.
You can nest try/except:
try:
email_list = EmailList.objects.get(domain=(cd['email'].split('#')[1]))
except:
try:
email_list = EmailList.objects.get(email=cd['email'])
except:
...do something else
If you are just using these try/except to do a simple test, it's worthwhile to wrap the try/except:
def get_or_none(model, **kwargs):
try:
return model.objects.get(**kwargs)
except model.DoesNotExist:
return None
Which gives you slightly more readable code:
if get_or_none(EmailList, domain='domain.com'):
...do something
elif get_or_none(EmailList, domain='domain.com'):
...do something
else:
...do something
As Ignacio mentioned in his answer you should always be explicit and catch only the exceptions you intend.
Related
I am trying to set up my password forgot in Django using built-in validators, but I am getting no response if reproduce errors, but it supposed to show up the errors others classes, how can I fix this?
forms.py
class RestorePasswordForm(SetPasswordForm):
new_password1 = forms.CharField(widget=forms.PasswordInput(attrs={'class':'form-control','placeholder':'Password','required': True}))
new_password2 = forms.CharField(widget=forms.PasswordInput(attrs={'class':'form-control','placeholder':'Password','required': True}))
views
def post(self, request,token):
form = RestorePasswordForm(request.POST)
if form.is_valid():
obj = token(token)
if obj:
this_user = User.objects.get(id=obj.email_id)
if not this_user:
return False
this_user.set_password(request.POST.get('new_password2'))
this_user.save()
obj.token = None
obj.save()
return JsonResponse({"message":form.errors})
browser response display
{"message": {}}
When you are validating a form you should always check if it is posting and then only process it. Also 'forms.errors' is going to give you HTML string, but you already are aware of that.
The reason you are getting empty reason is because nothing is being posted to the form.
def post(self, request,token):
if request.method=="POST":
form = RestorePasswordForm(request.POST)
if form.is_valid():
obj = token(token)
if obj:
this_user = User.objects.get(id=obj.email_id)
if not this_user:
return False
this_user.set_password(request.POST.get('new_password2'))
this_user.save()
obj.token = None
obj.save()
else:
print("you didn't post anything to form")
return JsonResponse({"message":form.errors})
I'm working on a django project where during registration, a user can submit a code to get special discounts. The validation of the discount codes is already working nicely, but I'm missing one beautifying aspect: After the user submits an invalid code I want to empty out the input field; i.e.:
def validate_code(value):
# check code for validity or raise ValidationError
class CodeForm(forms.Form):
code = forms.CharField(validators=[validate_code])
# in my views.py
def code_verification_view(request):
if request.method == 'POST':
form = CodeForm(request.POST)
if form.is_valid():
# proceed with given code
else:
# modify form to return an empty html input for the code field
# this is where I'm stuck
form.fields['code'].value = ''
# ... render the form in a template
The end result should be a form with an empty input field, but the validation errors showing. The behavior should be similar to how password input fields are emptied if the form verification fails.
EDIT: I solved the problem, but in a very hacky way:
see https://stackoverflow.com/a/46564834/8572938
I'd appreciate a proper solution that does not rely on accessing protected members of the form.
the key is to reset form variable
form = CodeForm(None)
in your code
def code_verification_view(request):
if request.method == 'POST':
form = CodeForm(request.POST)
if form.is_valid():
# proceed with given code
else:
form = CodeForm(None)
Just render your template, if your form is not valid, it will show error, In case if it is valid process your data
def code_verification_view(request):
if request.method == 'POST':
form = CodeForm(request.POST)
if form.is_valid():
// process your data
else:
form.data['field'] = None
return render(request, template_name, {'form': form})
Make a field validation in your form definition:
class CodeForm(forms.Form):
code = forms.CharField(validators=[validate_code])
def clean_code(self):
code = self.cleaned_data(code)
error = # some of your process
if error:
self.fields['code'] = None
raise forms.ValidationError('...')
else:
return code
And remove the else part in your view, instead you want to do something else. If you just want to display the form with error, the raise forms.ValidationError will do it.
You can in django form add a clean_<field_name> to control each field as you like.
More info here
I found a way that works, but it's quite dirty:
old_form = CodeForm(request.POST)
form = CodeForm()
if old_form.is_valid():
# ...
else:
form._errors = old_form._errors
# pass form into the rendering context
This way, I get a clean form with the preserved errors.
While it does the job, it is clearly an ugly hack.
I have settings form page. If user filled the form once; it must display those values. But if there is no data [first time] I get query error. I need that query, because the form data must be written as related with current user [logged in].
here is my view part :
#login_required(login_url='/login/')
def profile_page(request,username):
query = Profile.objects.get(owner__username = username) ##error!
if request.method == 'POST':
form = profile_form(request.POST,instance=query)
form.save()
return HttpResponseRedirect('/admin/')
else:
form = profile_form(instance=query)
return render_to_response('profile_save.html',{'form':form},context_instance = RequestContext(request))
I think I need to check the model and if it is empty I should do something different.
I am stuck.
Thank you
You want to make use of the .exists() queryset option
#login_required(login_url='/login/')
def profile_page(request,username):
form = profile_form()
if Profile.objects.get(owner__username = username).exists():
query = Profile.objects.get(owner__username = username)
if request.method == 'POST':
form = profile_form(request.POST,instance=query)
form.save()
return HttpResponseRedirect('/admin/')
else:
form = profile_form(instance=query)
return render_to_response('profile_save.html',{'form':form},context_instance = RequestContext(request))
see QuerytSet API reference for more information
You just need to wrap that get query in try ... except and set instance to none, like this.
from django.core.exceptions import ObjectDoesNotExist
#login_required(login_url='/login/')
def profile_page(request,username):
try:
query = Profile.objects.get(owner__username = username)
#to be more specific you can except ProfileObjectDoesNotExist
except ObjectDoesNotExist:
query = None #Doesn't exist, set to None
if request.method == 'POST':
form = profile_form(request.POST,instance=query)
form.save()
return HttpResponseRedirect('/admin/')
else:
form = profile_form(instance=query)
return render_to_response('profile_save.html',{'form':form},
context_instance = RequestContext(request))
I think i may have use get_or_create for this purpose.
Profile.objects.get_or_create(owner__username = username)
I need to call an API function after validating a form with is_valid(). This API call can still throw exceptions which in turn may kind of invalidate a field in the form.
How can I do that? I'm looking for something like that:
def smstrade(request):
if request.method == "POST":
form = SomeForm(request.POST)
if form.is_valid():
try:
api_call(...)
except SomeException:
form["field"].set_valid(False)
Bit late, but you can invalidate the form and add display the appropriate messages by setting form._errors
>>> f.is_valid()
True
>>> f._errors['my_field'] = ['This field caused a problem']
>>> f.is_valid()
False
>>> str(f)
... <ul class="errorlist"><li>This field caused a problem</li></ul>
I needed to do this with FormView.form_valid() methods and models with unique fields
def form_valid(self, form):
obj = User(**form.cleaned_data)
try:
obj.save()
except IntegrityError:
form._errors['username'] = ['Sorry, already taken']
return super(MyView, self).form_invalid(form)
return super(MyView, self).form_valid(form)
It is better to override the clean method for the field you are interested in and add your logic there. That way, you can output the appropriate error message as well.
Sounds like you just need a variable to store a valid state outside of the form object.
def smstrade(request):
if request.method == "POST":
form = SomeForm(request.POST)
valid = form.is_valid()
if valid:
try:
api_call(...)
except SomeException:
valid = False
if valid: # still valid?
print "VALID!"
But really it seems like you should be putting this in the form itself, so that you only need to call is_valid() once. The only complication would be if you needed access to the request object.
class MyForm(forms.Form):
def clean(self):
cd = super(MyForm, self).clean()
try:
api_call
except Exception:
raise forms.ValidationError("API Call failed")
return cd
# view..
if form.is_valid():
print "api call success and the rest of the form is valid too."
In case you really want to trigger the form validation by calling is_valid a second this is the way you can do it (Django 1.4)
form = MyForm(data)
form.is_valid()
# ...
form._errors = None
form.is_valid() # trigger second validation
I'm trying to catch an exception but does not work.This is the code in my view:
#login_required
def activities_edit(request, edit):
ActivityFormSet = modelformset_factory(Activity, can_delete=True)
act_edit= Activity.objects.filter(campaing=1).get(pk=edit)
try:
if act_edit:
if request.method == 'POST':
formset = ActivityFormSet(request.POST, request.FILES, queryset=Activity.objects.filter(pk=edit))
if formset.is_valid():
formset.save()
return HttpResponseRedirect('/activities/')
else:
formset = ActivityFormSet(queryset=Activity.objects.filter(pk=edit))
except act_edit.DoesNotExist:
return HttpResponseRedirect('/activities/')
I was also trying with: " except act_edit.DoesNotExist: "
but the error persists "Activity matching query does not exist."
Any idea?
Thanks!
You need to move the statement that can cause the exception in the body of the try: clause.
The syntax is "try: something catch stuff:" your something is above the try:
act_edit= Activity.objects.filter(campaing=1).get(pk=edit)
try:
Should be
try:
act_edit = Activity.objects.filter(campaing=1).get(pk=edit)
You have a little bit of redundancy catching the exception means you don't have to check if act_edit is empty because if it is empty it will raise DoesNotExist. Also the model has the DoesNotExist not the instance.
#login_required
def activities_edit(request, edit):
ActivityFormSet = modelformset_factory(Activity, can_delete=True)
try:
act_edit= Activity.objects.filter(campaing=1).get(pk=edit)
if request.method == 'POST':
formset = ActivityFormSet(request.POST, request.FILES, queryset=Activity.objects.filter(pk=edit))
if formset.is_valid():
formset.save()
return HttpResponseRedirect('/activities/')
else:
formset = ActivityFormSet(queryset=Activity.objects.filter(pk=edit))
#HttpResponse not returned error here.
except Activity.DoesNotExist:
return HttpResponseRedirect('/activities/')