Django валидация ajax форм

102
13 июля 2021, 15:50

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

Все работает информация в БД записывается формы подгружаются, но есть две проблемы.

  • 1) Валидация полей работает, информация не записывается в БД если не прошла валидацию, но я не понимаю как это сообщить пользователю, на данный момент пользователь никак не оповещается о том что он ввел непарвильную информацию в формы.
  • 2) При отправки формы в консоль вываливаются ошибки непонятной природы, на что влияют данные ошибки установить не удалось. Вывод ошибки очень длинный, но

ConnectionAbortedError: [WinError 10053] Программа на вашем хост-компьютере разорвала установленное подключение

Код Forms.py

class OperationAssembly(forms.Form):
def __init__(self, *args, **kwargs):
    quantity_of_fileds = kwargs.pop('quantity_of_fileds')
    self.current_user_id = kwargs.pop('current_user_id')
    super(OperationAssembly, self).__init__(*args, **kwargs)
    self.fields["start_date"] = forms.DateTimeField(initial=now, label='Дата сборки')
    self.fields['type_of_defect'] = forms.CharField(initial="Годная", label='Причина отбраковки', required=False)
    self.fields['serial_number'] = forms.CharField(label="Серийный номер")
    self.fields['type_of_product'] = ModelChoiceField_Label2(queryset=Type_of_product.objects.all(),
                                                             empty_label=None, label="Тип изделия")
    for i in range(1, quantity_of_fileds + 1):
        self.fields["Component {}".format(i)] = forms.CharField(label="С/Н Компонента №{}".format(i))
def clean_type_of_defect(self):
    data = self.cleaned_data['type_of_defect']
    if data == '':
        return 3
    else:
        return Type_of_defect.objects.get_or_create(label=data)[0].id
def clean(self):
    super(OperationAssembly, self).clean()
    # Сравниваю каждое поле с каждым на повторы
    for field1, field2 in itertools.combinations(self.fields, 2):
        if self.cleaned_data[field1] == self.cleaned_data[field2]:
            raise forms.ValidationError("Поля формы не должны повторяться")
def list_of_assembly(self):
    list_components = []
    for k in self.fields:
        if k.startswith("Component"):
            list_components.append(self.cleaned_data[k])
    # Получаем словарь с списком последних id из внесенных деталей
    temp_query = Product.objects.filter(serial_number__in=list_components).values('serial_number').annotate(max_id=Max('id'))
    return temp_query
def save(self):
    add_product = Product.objects.create(serial_number=self.cleaned_data['serial_number'],
                                         type_of_product_id_id=self.cleaned_data['type_of_product'].id)
    add_product.save()
    new_product_id = add_product.id
    list_of_assembly = self.list_of_assembly()
    for i in list_of_assembly:
        connection.cursor().execute('insert into "mainapp_product_Assembly" (from_product_id, to_product_id)'
                                    ' values ({},{});'.format(new_product_id, i["max_id"]))
    add_operation = Operation_history.objects.create(start_date=self.cleaned_data['start_date'],
                                                     end_date=self.cleaned_data['start_date'],
                                                     employess_id_id=self.current_user_id.id,
                                                     type_of_defect_id_id=self.cleaned_data['type_of_defect'],
                                                     type_of_operation_id_id=6)
    add_operation.save()
    list_of_char = Characteristic_of_product.objects.filter(type_of_product_id_id=self.cleaned_data['type_of_product'].id)
    for char in list_of_char:
        add_char = Product_2_characteristic.objects.create(value="NULL",
                                                           characteristic_of_product_id_id=char.id,
                                                           operation_history_id_id=add_operation.id,
                                                           product_id_id=add_product.id)
        add_char.save()

Код views.py

def operation_assembly_form(request, quantity_of_fields):
current_form = str(base64.b64encode(bytearray(str(uuid.uuid4()).encode("utf-8"))))[2:-1]
if request.method == "GET":
    form = OperationAssembly(quantity_of_fileds=quantity_of_fields,
                             current_user_id=Employess.objects.get(user_id=request.user.id))
elif request.method == "POST":
    form = OperationAssembly(request.POST, quantity_of_fileds=quantity_of_fields,
                             current_user_id=Employess.objects.get(user_id=request.user.id))
    if form.is_valid():
        form.save()
    else:
        return HttpResponse(form.errors.as_json(), content_type='application/json')
return render(request, 'operation_assembly_form.html', {'form': form, 'current_form': current_form,
                                                        'quantity_of_fields': quantity_of_fields})

Код подгружаемого html блока

    <div id="{{ current_form }}">
<form class="allforms" id="operation_formset" method="POST" action="/mainapp/add/operation/assembly/form/{{ quantity_of_fields }}/">
{% csrf_token %}
<table>
    <tr>
        {% for field in form %}
        <td>
        {{ form.errors }}
        {{ field.label_tag }}
        </td>
        {% endfor %}
    </tr>
    <tr>
        {% for field in form %}
        <td>
        {{ form.errors }}
        {{ field }}
        </td>
        {% endfor %}
        <td>
        <button type="button" id="{{ current_form }}_plus" class="btn btn-light">+</button>
        </td>
        <script>
            $('#{{ current_form }}_plus').on('click', function() {
                $.get('/mainapp/add/operation/assembly/form/' + ({{ quantity_of_fields }} + 1) + '/', function(data) {
                    $('#{{ current_form }}').html(data);
                });
                });
        </script>
    </tr>
</table>
</form>
</div>

Код основной html старницы

    {% extends "operation_menu.html" %}
{% block chosen_operation %}
<h1>Провести операции:</h1>
    <div id="add_form"></div>
    <button id="plus" class="btn btn-light">+</button>
    <button id="save" class="btn btn-light">Внести информацию о изделиях</button>
    <script>
    $('#plus').click(function() {
        $.ajax({
           url: 'form/1',
           success: function(data){
           $('#add_form').append(data);
           }
        });
    });
    $("#save").click(function(){
        $('.allforms').each(function(){
            valuesToSend = $(this).serialize();
            form_id = $(this).find('button');
            $.ajax($(this).attr('action'),
                {
                method: $(this).attr('method'),
                data: valuesToSend,
                success: function (data) {
                    console.log(data)
                    console.log(form_id.attr('id').slice(0, 48))
                    console.log($(this).attr('action'))
                    if (data.__all__) {
                        $.get($(this).attr('action'), function(data) {
                        $('#form_id').html(data);
                    });
          }
        }
                }
            )
        });
    });
    </script>
{% endblock %}
Answer 1

views.py

if form.is_valid():
    form.save()
else:
    response = {}
    for k in form.errors:
        response[k] = form.errors[k][0]
    return JsonResponse({"response": response, 'result': 'error'})

В js-файл в ajax запрос в success добавьте условие

if (data.result == 'error') {
  //Логика для вывода ошибок
}
Answer 2

На данный момент нужного эффекта в поведении формы я добился следующим кодом.

views.py

def operation_assembly_form(request, quantity_of_fields):
current_form = str(base64.b64encode(bytearray(str(uuid.uuid4()).encode("utf-8"))))[2:-1]
if request.method == "GET":
    form = OperationAssembly(quantity_of_fileds=quantity_of_fields,
                             current_user_id=Employess.objects.get(user_id=request.user.id))
elif request.method == "POST":
    form = OperationAssembly(request.POST, quantity_of_fileds=quantity_of_fields,
                             current_user_id=Employess.objects.get(user_id=request.user.id))
    if form.is_valid():
        form.save()
return render(request, 'operation_assembly_form.html', {'form': form, 'current_form': current_form,
                                                        'quantity_of_fields': quantity_of_fields})

Код общего шаблона страницы.

{% extends "operation_menu.html" %} 
 
{% block chosen_operation %} 
<h1>Провести операции:</h1> 
    <div id="add_form"></div> 
    <button id="plus" class="btn btn-light">+</button> 
    <button id="save" class="btn btn-light">Внести информацию о изделиях</button> 
    <script> 
    $('#plus').click(function() { 
        $.ajax({ 
           url: 'form/1', 
           success: function(data){ 
           $('#add_form').append(data); 
           } 
        }); 
    }); 
    $("#save").click(function(){ 
        Can_i_redirect = 0 
        $('.allforms').each(function(){ 
            valuesToSend = $(this).serialize(); 
            form_id = $(this).find('button'); 
            $.ajax($(this).attr('action'), 
                { 
                method: $(this).attr('method'), 
                data: valuesToSend, 
                context:this, 
                async:false, 
                success: function (data) { 
                console.log(data.slice(63, -6)) 
                    $(this).html(data.slice(63, -6)); 
                    console.log(data.indexOf('errorlist')) 
                    if (data.indexOf('errorlist') != -1) { 
                        Can_i_redirect += 1 
                        console.log(Can_i_redirect) 
                    } 
          } 
        }); 
        console.log(Can_i_redirect) 
        if (Can_i_redirect == 0) { 
           $(location).attr('href','/mainapp/add/operation/assembly/') 
        } 
    }); 
        }); 
    </script> 
{% endblock %}

Код вставляемых шаблонов.

    <div id="{{ current_form }}"> 
    <form class="allforms" id="operation_formset" method="POST" action="/mainapp/add/operation/assembly/form/{{ quantity_of_fields }}/"> 
    {% csrf_token %} 
    <table> 
        <tr> 
            {% for field in form %} 
            <td> 
            {{ field.errors }} 
            {{ field.label_tag }} 
            </td> 
            {% endfor %} 
        </tr> 
        <tr> 
            {% for field in form %} 
            <td> 
            {{ field }} 
            </td> 
            {% endfor %} 
            <td> 
            <button type="button" id="{{ current_form }}_plus" class="btn btn-light">+</button> 
            </td> 
            <script> 
                $('#{{ current_form }}_plus').on('click', function() { 
                    $.get('/mainapp/add/operation/assembly/form/' + ({{ quantity_of_fields }} + 1) + '/', function(data) { 
                        $('#{{ current_form }}').html(data); 
                    }); 
                    }); 
            </script> 
        </tr> 
    </table> 
    </form> 
    </div>

READ ALSO
Как правильно писать if else?

Как правильно писать if else?

Вопрос про стандарты if и else

330
sf::Vector2f в классе можно ли?

sf::Vector2f в классе можно ли?

Ругается на Vector2f хотя если не в классе писать все ок

211
Как передать информацию по локальной сети WinSock2?

Как передать информацию по локальной сети WinSock2?

Есть сервер он получает данные от клиента

103
как менять указатель на класс

как менять указатель на класс

есть 2 класса и указатель:

108