Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to raise normal Django form validation through ajax?

I found a similar question which is quite a bit outdated. I wonder if it's possible without the use of another library.

Currently, the forms.ValidationError will trigger the form_invalid which will only return a JSON response with the error and status code.
I have an ajax form and wonder if the usual django field validations can occur on the form field upon an ajax form submit.

My form triggering the error:

class PublicToggleForm(ModelForm):
    class Meta:
        model = Profile
        fields = [
            "public",
        ]
    def clean_public(self):
        public_toggle = self.cleaned_data.get("public")
        if public_toggle is True:
            raise forms.ValidationError("ERROR")  
        return public_toggle  

The corresponding View's mixin for ajax:

from django.http import JsonResponse

class AjaxFormMixin(object):

    def form_invalid(self, form):
        response = super(AjaxFormMixin, self).form_invalid(form)
        if self.request.is_ajax():
            return JsonResponse(form.errors, status=400)
        else:
            return response

    def form_valid(self, form):
        response = super(AjaxFormMixin, self).form_valid(form)
        if self.request.is_ajax():
            print(form.cleaned_data)
            print("VALID")
            data = {
                'message': "Successfully submitted form data."
            }
            return JsonResponse(data)
        else:
            return response

The View:

class PublicToggleFormView(AjaxFormMixin, FormView):
    form_class = PublicToggleForm
    success_url = '/form-success/'  

On the browser console, errors will come through as a 400 Bad Request, followed by the responseJSON which has the correct ValidationError message.

Edit: Any way to get the field validation to show client-side?

edit: Additional code:

Full copy of data received on front-end:

{readyState: 4, getResponseHeader: ƒ, getAllResponseHeaders: ƒ, setRequestHeader: ƒ, overrideMimeType: ƒ, …}
abort:
ƒ (a)
always:
ƒ ()
catch:
ƒ (a)
done:
ƒ ()
fail:
ƒ ()
getAllResponseHeaders:
ƒ ()
getResponseHeader:
ƒ (a)
overrideMimeType:
ƒ (a)
pipe:
ƒ ()
progress:
ƒ ()
promise:
ƒ (a)
readyState:
4
responseJSON:
public:
["ERROR"]
__proto__:
Object
responseText:
"{"public": ["ERROR"]}"
setRequestHeader:
ƒ (a,b)
state:
ƒ ()
status:
400
statusCode:
ƒ (a)
statusText:
"Bad Request"
then:
ƒ (b,d,e)
__proto__:
Object  

The form in the template is rendered using Django's {{as_p}}:

{% if request.user == object.user %}
        Make your profile public?
        <form class="ajax-public-toggle-form" method="POST" action='{% url "profile:detail" username=object.user %}' data-url='{% url "profile:public_toggle" %}'>

            {{public_toggle_form.as_p|safe}}
        </form>
    {% endif %}  

Javascript:

$(document).ready(function(){
    var $myForm = $('.ajax-public-toggle-form')
    $myForm.change(function(event){
        var $formData = $(this).serialize()
        var $endpoint = $myForm.attr('data-url') || window.location.href // or set your own url

        $.ajax({
            method: "POST",
            url: $endpoint,
            data: $formData,
            success: handleFormSuccess,
            error: handleFormError,
        })
    })

    function handleFormSuccess(data, textStatus, jqXHR){
        // no need to do anything here
        console.log(data)
        console.log(textStatus)
        console.log(jqXHR)
    }

    function handleFormError(jqXHR, textStatus, errorThrown){
        // on error, reset form. raise valifationerror
        console.log(jqXHR)
        console.log("==2" + textStatus)
        console.log("==3" + errorThrown)
        $myForm[0].reset(); // reset form data
        }
})
like image 399
Jay Jung Avatar asked Nov 07 '22 10:11

Jay Jung


1 Answers

Sou you have your error response in JSON formatted as {field_key: err_codes, ...}. Then all you have to do is for example create <div class="error" style="display: none;"></div> under every rendered form field, which can be done by manually rendering the form field by field or you can create a block with errors below the form such as:

<div id="public_toggle_form-errors" class="form-error" style="display: none;"><div>

add some css to the form:

div.form-error {
    margin: 5px;
    -webkit-box-shadow: 0px 0px 5px 0px rgba(255,125,125,1);
    -moz-box-shadow: 0px 0px 5px 0px rgba(255,125,125,1);
    box-shadow: 0px 0px 5px 0px rgba(255,125,125,1);
}

so it'll look like something wrong happened, and then add to the handleFormError function code:

function handleFormError(jqXHR, textStatus, errorThrown){
    ...
    $('#public_toggle_form-errors').text(textStatus.["public"]);
    $('#public_toggle_form-errors').show();
    ...
}

I think you'll get the idea.

like image 64
Kryštof Řeháček Avatar answered Nov 14 '22 21:11

Kryštof Řeháček