JavaRush /Курси /Модуль 3: Django /Використання inline-форм для редагування пов'язаних об'єк...

Використання inline-форм для редагування пов'язаних об'єктів

Модуль 3: Django
Рівень 13 , Лекція 8
Відкрита

Щоб зрозуміти, навіщо нам потрібні inline-форми, уяви собі ситуацію. У нас є модель Автор і модель Книга. Кожна книга пов'язана з автором через зовнішній ключ author. Під час створення нового автора ми хочемо також ввести інформацію про його книги. Замість того, щоб створювати форму для автора, а потім окремо форми для кожної книги, ми можемо вбудувати форму для Книг прямо у форму для Автора. Це і є inline-форма.

Django надає зручний інструмент для реалізації подібних задач — inline formsets. Inline-форми працюють на основі зв'язків моделей (ForeignKey і OneToOne) і дозволяють легко додавати, редагувати або видаляти пов'язані записи.

Основні випадки використання inline-форм

Inline-форми особливо корисні, коли потрібно:

  • Керувати пов'язаними об'єктами прямо з основної форми (наприклад, книги автора, замовлення клієнта або коментарі під постом).
  • Спростити інтерфейс для користувача, виключивши необхідність переходу між кількома формами.
  • Переконатися, що дані для пов'язаних моделей обробляються атомарно (або зберігаються всі дані, або нічого).

Крок 1: Підготовка моделей

Для демонстрації ми створимо проєкт з двома моделями: Author (автор) і Book (книга). Кожна книга пов'язана з певним автором через ForeignKey.

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField()

    def __str__(self):
        return self.name


class Book(models.Model):
    title = models.CharField(max_length=200)
    publish_date = models.DateField()
    author = models.ForeignKey(Author, related_name='books', on_delete=models.CASCADE)

    def __str__(self):
        return self.title

Тут:

  • Author — модель для зберігання інформації про автора.
  • Book — модель для книг. Кожна книга пов'язана з автором через ForeignKey.

Крок 2: Створення FormSet

Django надає спеціальний клас inlineformset_factory для автоматичного створення inline-форм. Цей клас пов'язує одну модель з іншою через зовнішній ключ.

Розглянемо, як створити inline-форму для управління книгами через форму автора:

from django.forms import modelformset_factory, inlineformset_factory
from django import forms
from .models import Author, Book

# Основна форма для моделі Author
class AuthorForm(forms.ModelForm):
    class Meta:
        model = Author
        fields = ['name', 'email']

# Inline-formset для моделі Book
BookFormSet = inlineformset_factory(
    Author,  # Батьківська модель
    Book,  # Модель, яка буде редагуватися через inline-форми
    fields=['title', 'publish_date'],  # Поля, доступні для зміни
    extra=1,  # Кількість додаткових порожніх форм
    can_delete=True  # Можливість видаляти пов'язані об'єкти
)

Зверніть увагу на параметри inlineformset_factory:

  • Перший аргумент — батьківська модель (у цьому випадку, Author).
  • Другий аргумент — модель, яка пов'язана з батьківською через ForeignKey (у цьому випадку, Book).
  • fields — список полів, які ми хочемо редагувати.
  • extra — кількість додаткових, порожніх форм у наборі.
  • can_delete — якщо True, дозволяє видаляти існуючі об'єкти.

Крок 3: Створення представлень (views)

Тепер створимо представлення для обробки форми автора та книг. Ми будемо використовувати AuthorForm і наш BookFormSet.

from django.shortcuts import render, redirect
from .models import Author
from .forms import AuthorForm, BookFormSet

def author_create_view(request):
    # Створюємо екземпляри форм
    if request.method == 'POST':
        author_form = AuthorForm(request.POST)
        book_formset = BookFormSet(request.POST)

        if author_form.is_valid() and book_formset.is_valid():
            # Зберігаємо автора
            author = author_form.save()

            # Прив'язуємо книги до автора і зберігаємо їх
            books = book_formset.save(commit=False)  # Отримуємо об'єкти книг, але поки не зберігаємо в базі
            for book in books:
                book.author = author
                book.save()

            return redirect('author_list')  # Перенаправлення після успішного збереження
    else:
        author_form = AuthorForm()
        book_formset = BookFormSet()

    return render(request, 'author_form.html', {
        'author_form': author_form,
        'book_formset': book_formset,
    })

Тут:

  1. Ми обробляємо як основну AuthorForm, так і BookFormSet одночасно.
  2. Після перевірки валідності обох форм зберігаємо об'єкт автора, а потім прив'язуємо книги до цього автора перед їх збереженням.

Крок 4: Шаблон для відображення inline-форм

Створимо шаблон author_form.html для відображення форми автора та форми книг.

<form method="post">
    {% csrf_token %}
    <h2>Автор</h2>
    {{ author_form.as_p }}

    <h2>Книги</h2>
    {{ book_formset.management_form }}
    {% for form in book_formset %}
        {{ form.as_p }}
    {% endfor %}

    <button type="submit">Зберегти</button>
</form>

Зверніть увагу на використання {{ book_formset.management_form }}. Це обов'язковий елемент при роботі з formset, оскільки він містить метадані про кількість форм, їх статуси та ідентифікатори.

Крок 5: Покращення роботи з порожніми формами

За замовчуванням Django додає лише порожні форми, вказані в параметрі extra. Ви можете налаштувати їх відображення, щоб користувачі могли додавати більше форм.

Приклад додавання порожньої форми для книг динамічно за допомогою JavaScript:

<script>
    const formsetContainer = document.querySelector('#form-container');
    const addFormButton = document.querySelector('#add-form-btn');

    addFormButton.addEventListener('click', function () {
        const totalForms = document.querySelector('#id_book_set-TOTAL_FORMS');
        const formIndex = totalForms.value; // Отримуємо індекс наступної форми
        const newForm = document.querySelector('.empty-form').cloneNode(true);

        newForm.innerHTML = newForm.innerHTML.replace(/__prefix__/g, formIndex);
        formsetContainer.appendChild(newForm);

        totalForms.value = parseInt(totalForms.value) + 1;
    });
</script>

Типові помилки при роботі з inline-формами

  1. Забули включити management_form у шаблон: якщо ви не додасте {{ book_formset.management_form }}, Django не зможе обробити форми коректно.
  2. Недопустимі дані в одній із форм: якщо хоча б одна форма у формсеті невалідна, весь процес збереження провалиться. Не забувайте використовувати метод is_valid() для перевірки даних.
  3. Не зв'язали об'єкти з батьківською моделлю: після збереження батьківської форми потрібно явно вказати зв'язок book.author = author, інакше дані не будуть коректно прив'язані.

Реальне використання

Inline-форми широко використовуються в адміністративних панелях, в e-commerce системах (наприклад, додавання товарів у замовлення), у блогах (створення постів з коментарями) та в CRM системах (робота з клієнтами та їх замовленнями). Вони спрощують взаємодію з користувачем і усувають необхідність у створенні багатьох окремих форм.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ