how do I pass form_obj.full_clean() errors back to the form?

Example:

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.

Review the docs at Form and field validation | Django documentation | Django and Working with forms | Django documentation | Django

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.

Here’s the code in my form:
Where would the ‘automatic error’ appear?

{% extends 'leads/base.html' %}
    
{% block title %}{{ title }}{% endblock %}

{% block content %}
    <h2>New Lead:</h2> 
    <form action="{{ form_action }}" method="post" autocomplete="off" novalidate>
        {% csrf_token %}
        {{ form.non_field_errors }}
        {% if id %}
        <input type="hidden" name="id" value="{{ id }}">
        {% endif %}
        <div class="fieldWrapper">
            {{ form.w_id.errors }}
            <label for="{{ form.w_id.id_for_label }}" class="field-label">w ID:</label>
            {{ form.w_id }}
        </div>
        <div class="fieldWrapper">
            {{ form.sector.errors }}
            <label for="{{ form.sector.id_for_label }}" class="field-label">Sector:</label>
            {{ form.sector }}
        </div>
        <div class="fieldWrapper">
            {{ form.zip.errors }}
            <label for="{{ form.zip.id_for_label }}" class="field-label">Zip:</label>
            {{ form.zip }}
        </div>
        <div class="fieldWrapper">
            {{ form.lat_lon.errors }}
            <label for="{{ form.lat_lon.id_for_label }}" class="field-label">Lat / Lon:</label>
            {{ form.lat_lon }}
        </div>
        <button type="submit">Submit</button>
    </form>
{% endblock %}

I’d need to see the complete view, model and the form involved here. It looks like you’re doing a lot of manual work that you don’t need to be doing.

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.

https://bpa.st/XAWGQ

A few notes that hopefully will help you make some progress.

1 Like

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 ModelForm is 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.)

Thanks ken,

I tried adding an extra field to the fields list (ModelForm) but I’m getting this error:

‘django.core.exceptions.FieldError: Unknown field(s) (full_name) specified for Author’

Could you be more specific? Where should I define the ‘extra’ field?

Tia!

Please post the actual code you are trying.

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 :slight_smile:

edit: Hmm I may be conflating ModelForm with CreateView… I’m using both!

Here’s the whole shabang:

https://bpa.st/UQMFY

This appears to be a completely different situation that your earlier issue, correct? If so, what is the specific problem happening here?

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 :slight_smile:

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.

I think I just got it…

Will report back.