Упорядочивание поля DELETE в наборе форм

У меня есть Django formset, который отображается в виде таблицы с одной формой на таблицу. Я хотел бы добавить флажок в первый столбец таблицы, чтобы пользователь мог отметить его, если он хочет удалить строку (форму).

У меня есть javascript для управления удалением строки набора форм (формы) и модификации формы управления на передней стороне, но у меня возникает проблема, когда я добавляю поле DELETE в форму. Я использовал ссылку на решение в modify DELETE widget, чтобы я мог добавить класс "delete" ко всем моим полям удаления для использования в CSS и JS на передней стороне. Когда поле DELETE добавлено, оно всегда является последним полем в форме. Я бы хотел, чтобы оно было первым.

models.py

class ModelOne(models.Model):
    attr_one = models.CharField(max_length=16)
    attr_two = models.CharField(max_length=16)

forms.py

class BaseFormOneFormset(BaseModelFormSet):
    def add_fields(self, form, index) -> None:
        super().add_fields(form, index)
        form.fields[DELETION_FIELD_NAME].widget = forms.CheckboxInput(attrs={'class':"delete"})
        form.fields["id"].widget=forms.HiddenInput(attrs={'class':'pk'}) 

class FormOne(forms.ModelForm):
    class Meta:
        model = ModelOne

    attr_one = forms.CharField(max_length=16,
                               required=True,
                               label="attr_one",
                               widget=forms.TextInput(attrs={'size':5,'class':'required '}))
    attr_two = forms.CharField(max_length=16,
                               required=True,
                               label="attr_two",
                               widget=forms.TextInput(attrs={'size':5,'class':'required '}))

views.py

def view_one(request):
    formset_factory_one = modelformset_factory( ModelOne,
                                                FormOne,
                                                formset=BaseFormOneFormset,
                                                extra=0,
                                                can_delete=True)
    formset_one = formset_factory_one(query_set=FormOne.objects.all(),
                                      prefix="formone")
    return render(request, "app_one/template_one.html",{"formset_one":formset_one})

template_one.html

<table id="tbl-id">
    <thead id="tbl-head-id">
        <tr>
            {% for form in formset_one|slice:":1" %}
                {% for field in form.visible_fields %}
                    <th>{{field.label|safe}}</th>
                {% endfor %}
            {% endfor %}
        </tr> 
    </thead>
    <tbody id="tbl-body-id">
        {% for form in formset_one %}
            <tr id="row{{forloop.counter0}}-id" class="formset-row">
                {% for field in form.visible_fields %}
                    <td>
                        {{field}}
                    </td>
                {% endfor %}
                {% for field in form.hidden_fields %}
                    <td hidden >{{field}}</td>
                {% endfor %}
            </tr>
        {% endfor %}
    </tbody>
</table>

Таблица результатов

изображение результирующей html-таблицы

Испробованные решения

Я уже пытался установить Form.field_order как в FormOne объявлении класса

class FormOne(forms.ModelForm):
    field_order = [ DELETION_FIELD_NAME, "attr_one", "attr_two"]

и в методе BaseFormset.add_fields

def add_fields(self, form, index) -> None:
    super().add_fields(form, index)
    form.fields[DELETION_FIELD_NAME].widget = forms.CheckboxInput(attrs={'class':"delete"})
    form.fields["id"].widget=forms.HiddenInput(attrs={'class':'pk'})
    form.field_order = [ DELETION_FIELD_NAME, "attr_one", "attr_two"]

В обоих случаях поле DELETE остается последним в заказе.

Хотя есть способы изменить порядок полей DELETE, переопределив функцию add_fields() набора форм, это довольно утомительно и сложно.

Однако, если ваша цель - просто вывести DELETE первым или в совершенно другой части страницы, вам повезло. Это легко сделать в шаблоне.

Поле delete может быть явно выведено в шаблоне с помощью {{ form.DELETE }}, что эквивалентно обычному выводу {{ field }}, который вы получаете при итерации через form.fields.

Простой пример, чтобы сначала отобразить поле DELETE:

{# this renders the DELETE field #}
{{ form.DELETE }} 

{# now render the other fields #}
{% for field in form.visible_fields %} 
  {# if check to prevent DELETE rendering twice #}
  {% if field.name != 'DELETE' %}  
    {{ field }}
  {% endif %}
{% endfor %}

Применим к коду из вашего вопроса:

<table id="tbl-id">
    <thead id="tbl-head-id">
        <tr>
            {% for form in formset_one|slice:":1" %}
                {% for field in form.visible_fields %}
                    <th>Delete</th>
                    {% if field.name !='DELETE' %}
                    <th>{{field.label|safe}}</th>
                    {% endif %}
                {% endfor %}
            {% endfor %}
        </tr> 
    </thead>
    <tbody id="tbl-body-id">
        {% for form in formset_one %}
            <tr id="row{{forloop.counter0}}-id" class="formset-row">
                    <td>
                        {{ form.DELETE }}
                    </td>
                {% for field in form.visible_fields %}
                    {% if field.name != 'DELETE' %}
                    <td>
                        {{field}}
                    </td>
                    {% endif %}
                {% endfor %}
                {% for field in form.hidden_fields %}
                    <td hidden >{{field}}</td>
                {% endfor %}
            </tr>
        {% endfor %}
    </tbody>
</table>
Вернуться на верх