Как использовать вложенные формы в Django

Django, python, formsets

Django Formsets управляет сложными повторяющимися полями форм в представлении. Используя формуляры, вы можете узнать, сколько форм было изначально, какие были изменены, а какие должны быть удалены. Подобно формам и моделям форм, Django предлагает наборы моделей форм, которые упрощают задачу создания набора форм для формы, обрабатывающей несколько экземпляров модели.

Django также предоставляет встроенные наборы форм, которые можно использовать для обработки набора объектов, принадлежащих общему внешнему ключу. В приведенных ниже примерах моделей мы можем написать inline-formset для обработки всех дочерних элементов, для родителя или всех адресов дочерних элементов.

# models.py

class Parent(models.Model):
    name = models.CharField(max_length=255)

class Child(models.Model):
    parent = models.ForeignKey(Parent)
    name = models.CharField(max_length=255)

class Address(models.Model):
    child = models.ForeignKey(Child)
    country = models.CharField(max_length=255)
    state = models.CharField(max_length=255)
    address = models.CharField(max_length=255)
# forms.py

from django.forms.models import inlineformset_factory

ChildrenFormset = inlineformset_factory(models.Parent, models.Child, extra=1)
AddressFormset = inlineformset_factory(models.Child, models.Address, extra=1)

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

Шаг 1: Создание базового inline-formset

# forms.py

from django.forms.models import BaseInlineFormSet

class BaseChildrenFormset(BaseInlineFormSet):
    pass

ChildrenFormset = inlineformset_factory(models.Parent,
                                        models.Child,
                                        formset=BaseChildrenFormset,
                                        extra=1)

Шаг 2. Прикрепление вложенного набор форм для каждой формы, как показано ниже. Супер-класс BaseInlineFormSet определяет метод add_fields, который отвечает за добавление полей для каждой формы в наборе форм. Также, здесь мы можем написать логику, чтобы связать вложенный набор форм.

# forms.py
class BaseChildrenFormset(BaseInlineFormSet):

    def add_fields(self, form, index):
        super(BaseChildrenFormset, self).add_fields(form, index)

        # save the formset in the 'nested' property
        form.nested = AddressFormset(
                        instance=form.instance,
                        data=form.data if form.is_bound else None,
                        files=form.files if form.is_bound else None,
                        prefix='address-%s-%s' % (
                            form.prefix,
                            AddressFormset.get_default_prefix()),
                        extra=1)

Примечание: Здесь мы создали новое свойство с именем "form.nested", которое содержит вложенный набор форм (AddressFormset).

Шаг 3. Обработка набора форм и вложенных наборов форм в представлениях

# views.py

def manage_children(request, parent_id):
    """Edit children and their addresses for a single parent."""

    parent = get_object_or_404(models.Parent, id=parent_id)

    if request.method == 'POST':
        formset = forms.ChildrenFormset(request.POST, instance=parent)
        if formset.is_valid():
            formset.save()
            return redirect('parent_view', parent_id=parent.id)
    else:
        formset = forms.ChildrenFormset(instance=parent)

    return render(request, 'manage_children.html', {
                  'parent':parent,
                  'children_formset':formset})

Шаг 4: Визуализация вложенного набора форм в шаблоне

# manage_children.html (Just formset display part)

{{ children_formset.management_form }}
{{ children_formset.non_form_errors }}

{% for child_form in children_formset.forms %}
    {{ child_form }}

    {% if child_form.nested %}
        {{ child_form.nested.management_form }}
        {{ child_form.nested.non_form_errors }}

        {% for nested_form in child_form.nested.forms %}
            {{ nested_form }}
        {% endfor %}

    {% endif %}

{% endfor %}

Есть несколько моментов, на которые нужно обратить внимание:

1. Проверка. При проверке формы в наборе форм нам также необходимо проверить ее подформы, которые находятся во вложенном наборе форм.

2. Сохранение данных. При сохранении формы также необходимо сохранять дополнения и изменения к формам во вложенном наборе форм.

Когда страница отправлена, мы вызываем formset.is_valid () для проверки форм. Мы переопределяем is_valid в нашем наборе форм, чтобы добавить проверку и для вложенных наборов форм.

# forms.py

class BaseChildrenFormset(BaseInlineFormSet):
    ...

    def is_valid(self):
        result = super(BaseChildrenFormset, self).is_valid()

        if self.is_bound:
            for form in self.forms:
                if hasattr(form, 'nested'):
                    result = result and form.nested.is_valid()

        return result

На этом проверка форм и вложенных форм завершается. Теперь нам нужно обработать сохраненное. Для этого необходимо переопределить метод save для сохранения родительского набора форм и всех вложенных наборов форм.

# forms.py

class BaseChildrenFormset(BaseInlineFormSet):
    ...

    def save(self, commit=True):

        result = super(BaseChildrenFormset, self).save(commit=commit)

        for form in self.forms:
            if hasattr(form, 'nested'):
                if not self._should_delete_form(form):
                    form.nested.save(commit=commit)

        return result

Метод save отвечает за сохранение форм в наборе форм, а также всех форм во вложенном наборе форм для каждой формы. Узнать больше о пакете с открытым исходным кодом Django CRM (Управление взаимоотношениями с клиентами) можно по этой ссылке .

Virtual hosting with 10 percent discount
Virtual hosting with 10 percent discount
EVILEG offers reliable hosting with a 10% discount for virtual hosting and 5% for VPS

Comments

Only authorized users can post comments.
Please, Log in or Sign up
TT
June 13, 2019, 7:01 p.m.
Taimoor Tanweer

C++ - Test 001. The first program and data types

  • Result:66points,
  • Rating points-1
TT
June 13, 2019, 6:51 p.m.
Taimoor Tanweer

C++ - Test 002. Constants

  • Result:75points,
  • Rating points2
ВМ
June 13, 2019, 12:30 p.m.
Ваня Мороз

C++ - Test 001. The first program and data types

  • Result:100points,
  • Rating points10
Last comments
МБ
June 20, 2019, 6:23 p.m.
Михаил Булатов

А если мне нужно сделать конект из дочернего qml?Сигнал работает только из main.qml
i
June 17, 2019, 6:10 a.m.
ingenfly

Только по осям xAxis2, уAxis2 значения начинаются с 0. Почему-то xAxis2 и xAxis не синхронизированы по данным. Ну и QCustomPlot последний.
June 16, 2019, 8:21 p.m.
Евгений Легоцкой

Добрый день. Ну точно также добавляете ту же самую информацию на ось xAxis2, только добавляете другое форматирование customPlot->xAxis2->setDateTimeFormat("hh:mm"); если я ...
EF
June 14, 2019, 1:56 p.m.
Egor Fomin

Спасибо за ваш ответ, у меня получилось реализовать это. Тем не менее появилась другая проблема, поэтому опять надеюсь на вашу помощь. Скажем, я уже выставил точки и они соеденены. Когда я нач...
d
June 13, 2019, 2:47 p.m.
damix

Можно классу, который описывает точку, добавить сигнал, который подавать (emit), когда точка перемещается (переопределить mouseMoveEvent или mouseReleaseEvent). Так вот эти сигналы у каждой из...
Now discuss on the forum
June 20, 2019, 9:30 a.m.
IscanderChe

Вернулся к этой задачке только-только, поэтому и не ответил ничего раньше.Как переопределить mouseReleaseEvent(QMouseEvent* event) у QTableView, если QTableView задан в ui? Или задавать QTabl...
I
June 19, 2019, 1:41 p.m.
Intruder

Всем добрый день. При разборе XML файла наткнулся на тег вот такого плана: <TagName attribute1="value1" attribute2="value2" /> При попытке проверить на наличие такого элеме...
June 19, 2019, 12:55 p.m.
Михаиллл

Скажите пожалуйста, как его в таком случае перемещать и удалять?
June 18, 2019, 7:50 p.m.
Дмитрий

Большое спасибо! SDK заработал.К сожалению удалось продвинутся только на один шаг. При сборке чистого проекта NDK выдаёт следующие ошибки C:\Android\ndk-bundle/toolchains/arm-linux-andr...
June 18, 2019, 4:59 p.m.
Михаиллл

Добрый день.В этом учебнике представлен код INSTALLED_APPS = ( ... 'rest_framework', 'snippets.apps.SnippetsConfig',) На строчке 'snippets.apps.SnippetsConf...
Looking for a Job?
25,000.00 руб. - 30,000.00 руб.
Разработчик Qt/C++
Barnaul, Altai Krai, Russia

For registered users on the site there is a minimum amount of advertising

EVILEG
About
Services
Join us
© EVILEG 2015-2019
Recommend hosting TIMEWEB