Есть ли способ установить разные идентификаторы для каждого ValidationError в Django? - PullRequest
1 голос
/ 13 февраля 2020

Вот так выглядит моя форма :

class TestForm(forms.ModelForm):
    class Meta:
        model = Restaurant
        fields = [
            'title',
            'content',
        ]

    def clean(self, *args, **kwargs):
        title = self.cleaned_data.get("title")
        content = self.cleaned_data.get("content")
        error_dict = {}

        if len(title) < 3:
            error_dict['title'] = ValidationError("testerror1")
        if len(content) < 3:
            error_dict['content'] = ValidationError('testerror2')

        if error_dict:
            raise ValidationError(error_dict)

Если я пытаюсь отправить форму с пустыми title и content, отображаются два сообщения об ошибке (testerror1, testerror2 ), они появляются над каждой меткой поля и выглядят так:

<ul class="errorlist">
    <li>test2</li>
</ul>

Но я хочу скрыть каждую из них, если клиент нажимает на ввод, поэтому я попытался с помощью Jquery:

$("#my_form_id").find('input, textarea').click(function() {
    $(this).closest('ul').hide();
})

Без успеха (не найдено ни одного элемента <ul>.

Мой вопрос: есть ли способ установить разные идентификаторы для каждой ошибки? Так что я могу управлять каждым по отдельности .

1 Ответ

2 голосов
/ 13 февраля 2020

Вы можете поставить уникальный идентификатор в каждом сообщении об ошибке, но это немного сложно, и я не уверен, насколько "безопасным" я бы это считал. Я думаю, что лучшей альтернативой для того, что вы хотите, является перебор полей в форме и рендеринг поля и сообщений об ошибках по одному. В конце этого поста описывается, как это сделать. Если вам действительно нужно поместить идентификаторы в сообщения об ошибках без циклического перебора полей в шаблоне ... хорошо, продолжайте читать.

Трудный путь

Чтобы получить больше, чем простое текстовое сообщение, отображаемое для каждого сообщения об ошибке (без разбивки формы в шаблоне), вам нужно предоставить класс ErrorList, альтернативный ModelForm. Класс ErrorList - это то, что выполняет рендеринг ошибок в HTML, поэтому, создав и используя подкласс, вы можете изменить то, что рендерится, включая добавление специального кода из самой ошибки ValidationError.

from django.forms.utils import ErrorList
from django.utils.html import format_html, format_html_join

# This overrides the ErrorList class to provide the additional rendering
#  features you want - in this example it only overrides it for the `ul` output
class ErrorListDerivative(ErrorList):
    def as_ul(self):
        if not self.data:
            return ''

        # Key part 1: the UL is now being rendered with a class for
        #  each of the errors which includes the error code from the
        #  ValidationError. You can then locate the UL by looking for that class.
        return format_html(
            '<ul class="{{}} {}">{{}}</ul>'.format(' '.join(('errorcode{}'.format(e.code) for e in self.data))),
            self.error_class,
            # Key Part 2: This adds the code from the validation error to the individual LIs
            format_html_join('', '<li class="errorforcode{}">{}</li>', ((e.code, e.message) for e in self.data))  
        )

Теперь, создав ErrorList, который отображает вещи так, как вы хотите, он должен использоваться TestForm.

class TestForm(forms.ModelForm):

    # This __init__ is what makes the ModelForm use the custom ErrorList class you've created. 
    #  The BaseForm from which ModelForm is derived (a few layers of inheritence deep) has an `error_class` argument to receive the class used to render errors. This just injects your custom class.
    def __init__(self, *args, **kwargs):
        kwargs_new = {'error_class': ErrorListDerivative}
        kwargs_new.update(kwargs)
        super().__init__(*args, **kwargs_new)

    class Meta:
        model = Restaurant
        fields = [
            'title',
            'content',
        ]

Затем, внутри вашей функции очистки TestForm, вы передаете дополнительный code в ValidationErrors

    def clean(self, *args, **kwargs):
        title = self.cleaned_data.get("title")
        content = self.cleaned_data.get("content")
        error_dict = {}

        # Key Part 3: Here we're including the custom error code in the 
        #  ValidationError, which will be rendered out
        if len(title) < 3:
            error_dict['title'] = ValidationError("testerror1", code='title')
        if len(content) < 3:
            error_dict['content'] = ValidationError('testerror2', code='content')

        if error_dict:
            # Must admit, not sure if adding a code here will do anything at all
            raise ValidationError(error_dict, code='3')

После того, как вы это сделаете, вывод HTML должен выглядеть примерно так:

<form id="my_form_id" method="post" novalidate="">
    <label for="id_title">Title:</label>
    <ul class="errorlist errorcodetitle">
        <li class="errorforcodetitle">testerror1</li>
    </ul><input type="text" name="title" value="ao" maxlength="100" required="" id="id_title">
    <label for="id_content">Content:</label>
    <ul class="errorlist errorcodecontent">
        <li class="errorforcodecontent">testerror2</li>
    </ul><input type="text" name="content" value="ao" maxlength="100" required="" id="id_content">
    <button type="submit">Submit</button>
</form>

Теперь, когда класс на этих UL, вы можете использовать имя поле, чтобы найти соответствующий UL и скрыть его.

$("#my_form_id").find('input, textarea').click(function(evt) {
    $('.errorcode' + this.name).hide();
})

Идиомати c Путь

Если вы не хотите go вниз по этой кроличьей норе, альтернативой является сделать что-то более похожее на пример в django документах для «зацикливания полей формы» (https://docs.djangoproject.com/en/3.0/topics/forms/#looping -over-the-form-s-fields ) Это не дает вам пользовательские классы (или идентификаторы, что вы в конечном итоге добавить) на й Сообщения об ошибках, но они гораздо более идиоматичны c.

Что-то вроде следующего ...

{% for field in form %}
    <div class="fieldWrapper">
        <div class="errorcode{{field.html_name}}">
            {{ field.errors }}
        </div>
        {{ field.label_tag }} {{ field }}
        {% if field.help_text %}
        <p class="help">{{ field.help_text|safe }}</p>
        {% endif %}
    </div>
{% endfor %}

Тогда вы можете использовать тот же jquery, как было описано выше:

$("#my_form_id").find('input, textarea').click(function(evt) {
    $('.errorcode' + this.name).hide();
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...