Formularios Django: si no es válido, muestra el formulario con un mensaje de error

112

I formularios Django, puede verificar si el formulario es válido:

if form.is_valid(): 
    return HttpResponseRedirect('/thanks/')

¿Pero me falta qué hacer si no es válido? ¿Cómo devuelvo el formulario con los mensajes de error? No veo el "más" en ninguno de los ejemplos.

usuario984003
fuente

Respuestas:

242

Si representa la misma vista cuando el formulario no es válido, en la plantilla puede acceder a los errores del formulario utilizandoform.errors .

{% if form.errors %}
    {% for field in form %}
        {% for error in field.errors %}
            <div class="alert alert-danger">
                <strong>{{ error|escape }}</strong>
            </div>
        {% endfor %}
    {% endfor %}
    {% for error in form.non_field_errors %}
        <div class="alert alert-danger">
            <strong>{{ error|escape }}</strong>
        </div>
    {% endfor %}
{% endif %}

Un ejemplo:

def myView(request):
    form = myForm(request.POST or None, request.FILES or None)
    if request.method == 'POST':
        if form.is_valid():
            return HttpResponseRedirect('/thanks/')
    return render(request, 'my_template.html', {'form': form})
Aamir Adnan
fuente
He añadido un ejemplo sencillo. Asegúrese de seguir el mismo enfoque que he mencionado.
Aamir Adnan
1
Veo. Devuelvo el mismo formulario en el que ingresé. La función is_valid () le ha agregado automáticamente los mensajes de error.
user984003
sí, lo tienes ahora mismo. Si no ha procesado el formulario manualmente, los errores se mostrarán automáticamente para cada campo.
Aamir Adnan
@AlexanderSupertramp myFormes una instancia de forms.Formo forms.ModelForm, lea sobre Django Forms
Aamir Adnan
¿Qué pasa si no tengo una vista ... por ejemplo, usando un formulario de administración estándar dentro del CMS? ¿Por ejemplo en una UNIQUE constraint failed:excepción?
geoidesic
19

views.py

from django.contrib import messages 

def view_name(request):
    if request.method == 'POST':
        form = form_class(request.POST)
        if form.is_valid():
            return HttpResponseRedirect('/thanks'/)
        else:
            messages.error(request, "Error")
return render(request, 'page.html', {'form':form_class()})

Si desea mostrar los errores del formulario que no sean válidos, simplemente ingrese {{form.as_p}} como lo que hice a continuación

page.html

<html>
    <head>
        <script>
            {% if messages %}
                {% for message in messages %}
                    alert(message);
                {% endfor %}
            {% endif %}
        </script>
    </head>
    <body>
        {{form.as_p}}
    </body>
</html> 
Catalina
fuente
¿Y luego que devuelvo? ¿Cómo llega esto a mi plantilla?
user984003
Actualizo mi código. También puede poner el mensaje de bucle for en su plantilla en lugar de un script, si lo desea.
Catherine
1
este es un enfoque genial, pero debe estar alerta ('{{mensaje}}');
amchugh89
¿Cómo pondría algo más descriptivo en el mensaje de error de la vista que 'Error' como lo ha hecho messages.error(request, "Error")?
cbuch1800
3
def some_view(request):
    if request.method == 'POST':
        form = SomeForm(request.POST)
        if form.is_valid():
            return HttpResponseRedirect('/thanks'/)
    else:
        form = SomeForm()
    return render(request, 'some_form.html', {'form': form})
Lukasz Koziara
fuente
3

ACTUALIZACIÓN: Se agregó una descripción más detallada de los errores del conjunto de formularios.


Form.errors combina todos los field y non_field_errors. Por lo tanto, puede simplificar el html a esto:

modelo

    {% load form_tags %}

    {% if form.errors %}
    <div class="alert alert-danger alert-dismissible col-12 mx-1" role="alert">
        <div id="form_errors">
            {% for key, value in form.errors.items %}
                <span class="fieldWrapper">
                    {{ key }}:{{ value }}
                </span>
            {% endfor %}
        </div>
        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
            <span aria-hidden="true">&times;</span>
        </button>
    </div>
    {% endif %}


If you want to generalise it you can create a list_errors.html which you include in every form template. It handles form and formset errors:

    {% if form.errors %}
    <div class="alert alert-danger alert-dismissible col-12 mx-1" role="alert">
        <div id="form_errors">

            {% for key, value in form.errors.items %}
                <span class="fieldWrapper">
                    {{ key }}:{{ value }}
                </span>
            {% endfor %}
        </div>
        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
            <span aria-hidden="true">&times;</span>
        </button>
    </div>
    {% elif formset.total_error_count %}
    <div class="alert alert-danger alert-dismissible col-12 mx-1" role="alert">
        <div id="form_errors">
            {% if formset.non_form_errors %}
                {{ formset.non_form_errors }}
            {% endif %}
            {% for form in formset.forms %}
                {% if form.errors %}
                    Form number {{ forloop.counter }}:
                    <ul class="errorlist">
                    {% for key, error in form.errors.items %}
                        <li>{{form.fields|get_label:key}}
                            <ul class="errorlist">
                                <li>{{error}}</li>
                            </ul>
                        </li>
                    {% endfor %}
                    </ul>
                {% endif %}
            {% endfor %}

        </div>
    </div>

    {% endif %}

form_tags.py

from django import template

register = template.Library()


def get_label(a_dict, key):
    return getattr(a_dict.get(key), 'label', 'No label')


register.filter("get_label", get_label)

Una advertencia: a diferencia de los formularios, Formset.errors no incluye non_field_errors.

P. Maino
fuente
0

simplemente puede hacer esto porque cuando inicializó el formulario contiene datos del formulario y datos no válidos también:

def some_func(request):
    form = MyForm(request.POST)
    if form.is_valid():
         //other stuff
    return render(request,template_name,{'form':form})

if generará el error en la plantilla si tiene alguno, pero los datos del formulario permanecerán como:

error_demo_here

Dinesh Kc
fuente
-1

Puede poner simplemente una variable de bandera, en este caso is_successed .

def preorder_view(request, pk, template_name='preorders/preorder_form.html'):
    is_successed=0
    formset = PreorderHasProductsForm(request.POST)
    client= get_object_or_404(Client, pk=pk)
    if request.method=='POST':
        #populate the form with data from the request
       # formset = PreorderHasProductsForm(request.POST)
        if formset.is_valid():
            is_successed=1
            preorder_date=formset.cleaned_data['preorder_date']
            product=formset.cleaned_data['preorder_has_products']
            return render(request, template_name, {'preorder_date':preorder_date,'product':product,'is_successed':is_successed,'formset':formset})



    return render(request, template_name, {'object':client,'formset':formset})

luego en su plantilla puede simplemente poner el código a continuación

{%if is_successed == 1 %}
<h1>{{preorder_date}}</h1>
<h2> {{product}}</h2>
{%endif %}
gtopal
fuente