Your app tracks leads. You have an html form to UPDATE a lead. You can change things like the lead’s name, description, etc. You can also change the lead_id, however it needs to be unique to the table.
i’m collecting form input via request.POST, validating the input at the form level, then I do a form.full_clean() to validate at the model level. The form.full_clean() is in a try/except block, with a ValidationError being raised if there’s an exception
my question is how to pass the “exception” to the form, so that it can be returned to the user for review?
def post(self, request, **args):
form = LeadForm(request.POST)
_id = request.POST['id']
context = {
'id': _id,
'form': form,
'form_action': reverse('leads:update', args=[_id])
}
if form.is_valid():
lead = Lead.objects.get(id=_id)
lead.w_id = form.cleaned_data['w_id']
lead.sector = form.cleaned_data['sector']
lead.zip = form.cleaned_data['zip']
lead.lat = form.cleaned_data['lat']
lead.lon = form.cleaned_data['lon']
# lead.user = User.objects.get(id=1)
try:
lead.full_clean()
except ValidationError as e:
******* THIS IS WHERE I NEED HELP ***********
print(e)
return render(request, 'leads/forms/lead.html', context)
lead.save()
return HttpResponse('POST method fired just fine!')
return render(request, 'leads/forms/lead.html', context)
So after some more digging, I came up with this solution, but… I hope there’s some Django snazzy way and not this clunky hack! All i’m really to do is dupe-check a field (some sort of validation)
try:
lead.full_clean()
except ValidationError as e:
print(e)
for i in e:
form.add_error(i[0], i[1])
That’s not how form validation works. While you raise the error in the clean process, it’s actually caught and handled by Django, and populates the error object stored within the form. You don’t need to add the error to the form, Django does it for you.
Have been reviewing the docs pretty much all day. I’m not sure what specifically I need to be reviewing here. Can you provide a code example? If I don’t have that try/except block the exception crashes the app.
Happy to share it all.
I’m new to Django so just trying to figure it out as I go. Have gone through the official tutorial and been reading the “docs” over the past 3 weeks with mixed success.
Here you go:
That’s the model, view, form, and template.
A few notes that hopefully will help you make some progress.
Your form is sufficiently close to your model that you would probably be better served with a ModelForm than a regular form. This would remove most of the needs for the assignment statements within your form.is_valid() block
This also means that you may be better served inheriting from the Django-provided UpdateView than creating your own UpdateView based upon View
You’re calling lead.full_clean() as a model method - that’s why you’re needing the try/except block here. If your form is a model form, then the form.is_valid function will call full_clean on the model as part of its test - making the errors part of the form and not needing to be handled separately.
If you want to see how small a basic set of form operations can be when using the Django-provided generic CBVs, see Build a Django CRUD App by Using Class-Based Views | by Fabrício Barbacena | Towards Data Science (Yes, this is shorter than anything you would probably actually build, but it is a minimal example that relies upon all the defaults. However, it does demonstrate the minimum of what’s required.)
I agree that getting a sufficient understanding the flow of events through the Django generic CBVs is not easy, but it is necessary if you’re going to use them effectively. I always point people toward the Classy Class-Based Views site and the CBV diagrams page. They are excellent resources for helping to understand the Django CBVs.
Thanks, that’s incredibly helpful!
It will take me some time to digest all that. I’d like to ask about one specific point which stands out immediately. The main reason I did all this manually is because I have a single lon_lat form field that’s parsed in the lon_lat validator into separate lon and lat fields (clean_lon_lat())
Would I be able to accomplish this using ModelForm and if so, would you provide some direction for how I might do that?
A ModelFormis a Form, but with some additional features. You’re still working with a Form, Django is just doing some (possibly most or all) of the form-related work for you.
What this means is that everything you can do in a Form, can also be done in a ModelForm, including:
Defining a form field named lat_lon,
Defining a clean_lat_lon method
You can then exclude the lon and lat fields from the list of fields to be automatically generated for the form. (Or use the include attribute to specifically list the fields needed.)
Side note: Again, a ModelForm is a form. How do you define a form field in a regular form? That’s also how you define a form field in a ModelForm when that field is not also a field in the model.
Doing exactly that. I tried to define a “full_name” field inside the ModelForm class, just as I did when I created a simple form and needed a field that wasn’t in the db:table. It doesn’t work.
When I tried simply adding “full_name” to the list of fields defined in the Meta section, I got the error I posted earlier. Also, when I added the form field the my (earlier) simple form, the form wasn’t being generated (as in the case of ModelForm), so this way is really much simpler and more convenient, provided its doable
edit: Hmm I may be conflating ModelForm with CreateView… I’m using both!
Well, I took your advice and decided to do this via ModelForm. I watched some videos, read some docs and I’m starting to ‘get it’. So I created a simple sandbox project just to test this out. I guess some wires got crossed in my head along the way. I used ModelForm but also the generic CBV CreateView. As I’m unfamiliar with either of these, I thought ModelForm was generating the form in the view. Wrong! It was CreateView. So this kinda, sorta simplifies my question a bit
So ModelForm doesn’t generate any form template, correct? So I still have to create that manually? Its just the validation that ModelForm is doing?
I guess I’d like some clarity on what specifically ModelForm is doing for me and also, how I would go about getting that extra “full_name” field into the template.