Удаление 'disabled' при сохранении изменений в наборе форм

Я создаю таблицу с набором форм из кверисета существующих объектов. Несколько полей в таблице должны изменяться, а другие поля должны отображаться, но не изменяться. Для отображения нередактируемых полей я использую виджет с 'disabled' в форме. GET-запрос делает все правильно. Но POST-запрос не сохраняет изменения в разрешенных полях, потому что неразрешенные поля являются обязательными и отключены, что блокирует отправку существующих значений. Я пытался использовать jQuery из example, но это не работает. Более того, я не понимаю, как я могу ссылаться на разные идентификаторы в наборе форм.

Модели:

class Order(models.Model):
    car = models.ForeignKey(
        Car,
        blank=True,
        null=True,
        on_delete=models.PROTECT,
        related_name='car_orders',
    )
    department = models.ForeignKey(
        Department,
        blank=False,
        null=False,
        on_delete=models.PROTECT,
        related_name='dep_orders',
    )
    ...

View

def orders_list(request, year, month, day):
    orders = Order.objects.filter(
        order_date__year=year, order_date__month=month, order_date__day=day
    )

    if request.method == 'POST':
        formset = OrderCloseFormSet(
            request.POST or None, queryset=orders, prefix='order'
        )
        if formset.is_valid():
            formset.save(commit=False)
            for form in formset:
                form.save()

    formset = OrderCloseFormSet(queryset=orders, prefix='order')

    context = {'orders': orders, 'formset': formset}

    return render(request, 'orders/orders_list.html', context)

Форма

class OrderCloseForm(forms.ModelForm):
    class Meta:
        model = Order
        fields = ( 
            'type_car',
            'department',
            ...
        )
        widgets = {
            'car': forms.Select(attrs={'style': 'width: 100%'}),
            'department': forms.Select(attrs={'disabled': 'True', 'style': 'width: 100%'}),
            ...
        }

Шаблон

<form method="post" action="{% url 'orders:orders_list' year month day %}">
        {% csrf_token %}

        {{ formset.management_form }}
        {{ formset.non_form_errors.as_ul }}
        <table class="table table-order" id="order-formset">

          {% for form in formset %}
            {% if forloop.first %}
              <thead>
                <tr>
                  {% for field in form.visible_fields %}
                    <th>{{ field.label|capfirst }}</th>
                  {% endfor %}
                </tr>
              </thead>
            {% endif %}

            <tbody>
              <tr>
               {% for field in form %}
                  <td>
                  {% if field == car %}
                    {% for hidden in form.hidden_fields %}
                     {{ hidden }}
                    {% endfor %}
                  {%  endif %}

                  {% if field == form.route_movement %}
                    <a href="{% url 'orders:order_detail' form.id.value %}">
                      {{ field|addclass:'input-box input-select' }}
                    </a>
                  {% else %}
                    {{ field|addclass:'input-box input-select' }}
                  {% endif %}
                  </td>
                 {% endfor %}
              </tr>
            </tbody>

          {% endfor %}
        </table>
  ...
</form>

<script>
  $('id_order-0-department').submit(function(e) {
    $(':disabled').each(function(e) {
        $(this).removeAttr('disabled');
    })
  });
</script>

В ответ получите id формы в наборе форм со следующим id:

<select name="order-0-department" disabled="True" id="id_order-0-department"></select>

Как сделать таблицу с редактируемыми полями, но сохраняющую данные в неизменных полях.

Установив disabled, вы только сделаете поле нередактируемым, так что люди все равно смогут подделать POST-запрос, который изменит поле.

Вы должны установить поле на .disabled = True, это не только пропустит поля, но и не позволит людям подделать POST-запрос, который каким-то образом изменит эти поля, например:

class OrderCloseForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['department'].disabled = True

    class Meta:
        model = Order
        fields = (
            'type_car',
            'department',
            # …
        )
        widgets = {
            'car': forms.Select(attrs={'style': 'width: 100%'}),
            'department': forms.Select(attrs={'style': 'width: 100%'}),
            # …
        }

На самом деле вид намного проще, чем сейчас: вы можете сохранить FormSet напрямую:

def orders_list(request, year, month, day):
    orders = Order.objects.filter(
        order_date__year=year, order_date__month=month, order_date__day=day
    )

    if request.method == 'POST':
        formset = OrderCloseFormSet(
            request.POST, request.FILES, queryset=orders, prefix='order'
        )
        if formset.is_valid():
            formset.save()  # commit=True
            return redirect('name-of-some-view')

    else:
        formset = OrderCloseFormSet(queryset=orders, prefix='order')

    context = {'orders': orders, 'formset': formset}
    return render(request, 'orders/orders_list.html', context)

Note: In case of a successful POST request, you should make a redirect [Django-doc] to implement the Post/Redirect/Get pattern [wiki]. This avoids that you make the same POST request when the user refreshes the browser.

Вернуться на верх