How to process Django validation errors - django

I'm having some trouble grokking Django forms and validation.
#views.py:
def create(request):
if request.method == 'POST':
form = CreateDocumentForm(request.POST)
if form.is_valid():
doc = Document.objects.create(name=form.cleaned_data['name'])
#snip
#forms.py:
class CreateDocumentForm(forms.ModelForm):
name = forms.CharField()
def clean_name(self):
cleaned_name = self.cleaned_data['name']
rgx = re.compile('^(\w|-|\.)+$')
if rgx.match(cleaned_name) == None:
raise ValidationError("invalidchars")
return cleaned_name
The logic is working properly, but I don't know how to tell which kind of VaidationError was raised. Also - This is handled by an Ajax request, so I won't be using templating in the repsonse. I need to get the status of what failed in this view.
thx

You generally won't see the ValidationErrors themselves. If you call form.is_valid, then the errors that occur during validation are all collected and returned to you as a dictionary, form.errors
You can check that dictionary for errors relating to any specific field. The result for any field with errors should be the string value of any ValidationErrors that were raised for that field.
In your view, then, if form.is_valid() returns False, then you can do this:
if 'name' in form.errors:
for err_message in form.errors['name']:
# do something with the error string

Related

Disabled field causes form validation error in Django

There is a case where I need to reuse the same ModelForm (The name's EngagementForm) in several cases, but wanted to disable some of the fields depends on the case. So I applied disabled inside my forms.py:
# Override Field Initialisation, used for disabling fields
def __init__(self, *args, **kwargs):
schedule_check = kwargs.pop('schedule', False)
super(EngagementForm, self).__init__(*args, **kwargs)
# If schedule argument set to True, disable the following fields
if schedule_check:
fields = (
'project_code',
'project_name',
'user_credential',
)
for i in fields:
self.fields[i].disabled = True
self.fields[i].required = False
However, Django throws validation error when I perform submit. The form.is_valid() returns False, and while inspecting errors using Pdb I saw this:
(Pdb) form.errors
{'project_code': ['This field is required.'],
'project_name': ['This field is required.'],
'user_credential': ['This field is required.']}
Check for their values, seems like disabled took away their values (or I might wrong here):
(Pdb) form['project_code'].value()
(Pdb)
I've tried few solutions found online (including feeding in initial values for each fields) yet none of these could fix the issue here.
FYI, this is the code in views.py:
def schedule_form(request, pk):
engagement = get_object_or_404(Engagement, pk=pk)
if request.method == "GET":
context = {
'title': "Schedule Task",
'form': EngagementForm(
instance=engagement,
schedule=True,
),
}
return render(request, 'forms/engagement_form.html', context)
else: # POST request
form = EngagementForm(request.POST, instance=engagement)
# Django thinks the form is invalid #
if form.is_valid():
form.save(update_fields=['schedule_date'])
return redirect('index')
else:
# Shows error message
# Django executed this block upon submit #
Much appreciated if I can get help from you guys, at least I should know what did I miss here.
The solution was mentioned in here. Since of the way HTML handles the POST method, all disabled fields won't passed to the backend, causes form validation error.
Solution #1 solved my issue, straightforward yet not elegant codewise. However, solution #2 is more elegant IMO yet did not solve the issue.

How can I delete form field data in django after submit

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.

django form errors before submit

My django form has errors in the initial page load, before the form even has a chance to be submitted.
My view:
def example_function(request):
if request.method == 'POST':
# the request is GET
else:
form = MyForm(user=request.user)
import pdb;pdb.set_trace()
return render_to_response('templates/example.html', locals(), context_instance=RequestContext(request),)
Where I have my pdb imported, in the console I can see that my form already has errors. The output of form.errors in my console is all the fields in the model which are set to not null.
(Pdb) form.errors
{'example_field_1': [u'This field is required.'], 'example_field_2': [u'This field is required.']}
The form has not submit yet, but I am still getting errors. Can someone explain?
I'm using django 1.4.
My form:
class MyForm(forms.ModelForm):
captcha = ReCaptchaField()
_readonly_template = form.TextInput(attrs={'readonly':'readonly'})
first_name = forms.CharField(widget = _readonly_tempalte)
def __init__(self, data=None, *args, **kwargs):
data = data or {}
if 'user' in kwargs:
user = kwargs['user']
del kwargs['user']
data.update({
'first_name' : user.first_name,
})
super(MyForm, self).__init__(data, *args, **kwargs)
class Meta:
model = MyModel
My model:
class MyModel(models.Model):
first_name = models.CharField(max_length=255)
example_field_1 = models.CharField(max_length=255)
example_field_2 = models.CharField(max_length=255)
https://docs.djangoproject.com/en/1.8/ref/forms/validation/
accessing the form.errors attribute will trigger the various form validation methods. Those errors shouldn't show up when you render the form.
I'm not sure how the user field is structured, but keep in mind that if you want the user name, you may want to change that from request.user to request.user.username.
I hope you resolved your issue, but in case you haven't, I had a similar issue which I was able to resolve by using "or None" when setting the form after checking if it is a POST (or GET) request.
In your case it looks like this may be a slightly different issue, but I wondered if this snippet might fix things up:
if request.method == "POST":
form = MyForm(request.POST or None)
# .. do stuff....
else: #.....this is a GET
data = {'user': request.user.username} #note this is changed to username
form = MyForm(data)
Not sure if still useful, but adding it here, as I just ran into this for my ChoiceField items within my form.
I was getting the same error messages, but eventually found out I had forgotten to ad 'or None' when initiating the form inside my view.
The initial code inside my view function that was displaying the error messages from the start:
form=FormName(request.POST)
I just added the 'or None' to it:
form=FormName(request.POST or None)
And all good after that.
Don't you need to do something like this
form = NameForm(request.POST)
Rather then attempting to use the user object to populate the form? Will the user object have an example_field_1 in it?
https://docs.djangoproject.com/en/1.8/topics/forms/
This is the normal behavior.
Some properties of fields are checked on client side. The error messages belong to the form, are part of the html but are not displayed until needed. It saves a client-server request.

Set django form as erroneous after is_valid()

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

Primary key check in django

I have this custom primary key in a model:
class Personal(models.Model):
name = models.CharField(max_length=20,primary_key=True)
email = models.EmailField(blank=True,null=True)
Now the thing i m not getting is, how can i create my view so that no duplicate record is entered? I searched this over online, but could find any technique to get the view created.
here is the code for views
def uregister(request):
errors = []
if request.method == 'POST':
if not request.POST.get('txtName', ''):
errors.append('Enter a Name.')
if not errors:
n = request.POST['txtName']
e = request.POST['txtEmail']
try:
per_job = Personal(name=n, email=e)
per_job.save()
except IntegrityError:
return render_to_response('gharnivas/register.html', {'exists': true}, context_instance=RequestContext(request))
return HttpResponseRedirect('/')
else:
return render_to_response('register.html', {'errors': errors}, context_instance=RequestContext(request))
How can i tel the user that, the name already exists?
Catch the inevitable exception upon saving, and tell them.
Use:
per_job.save(force_insert=True)
What you are looking for is Form and Form Validation:
http://docs.djangoproject.com/en/dev/topics/forms/?from=olddocs#customizing-the-form-template
Define a PersonalForm class, move your validation checks in form field definitions or clean*() methods, then show error fields from form in template.
Django book link for form processing:
http://www.djangobook.com/en/2.0/chapter07/