Уяви, що ти створюєш блог, де є пости і категорії. Кожен пост відноситься до однієї або кількох категорій (зв'язок ManyToMany). Блогеру потрібен інтерфейс, щоб не тільки додати новий пост, але й відредагувати категорії прямо в тій самій формі. Як це зробити? Використовуй форми для пов'язаних моделей!
У реальних проєктах такі форми використовуються всюди, де є необхідність працювати з даними з кількох таблиць/моделей бази даних. Це ж набагато зручніше для користувачів — один інтерфейс для всього.
Підготовка: створимо моделі та зв'яжемо їх
Перед тим як почати роботу з формами, спочатку налаштуємо моделі. У якості прикладу будемо працювати з блогом.
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
categories = models.ManyToManyField(Category, related_name='posts')
def __str__(self):
return self.title
Тут у нас дві моделі: Category для категорій та Post для постів. Пости можуть бути пов'язані з кількома категоріями через поле categories (зв'язок типу ManyToMany).
Підхід №1: Працюємо з окремими формами
Перший підхід до роботи зі зв'язаними моделями — це використання окремих форм для кожної моделі. Наприклад, форма для постів та форма для категорій. Почнемо з простого.
Форма для створення постів
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content', 'categories']
Форма для створення категорій
from django import forms
from .models import Category
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
fields = ['name']
Кожна модель тут має свою окрему форму. Поле categories автоматично відображається у формі PostForm як MultipleChoiceField, де користувач зможе обирати з існуючих категорій.
Кроки у представленнях
Тепер потрібно написати представлення, щоб обробити обидві форми. Роботу з категоріями та постами можна розділити.
from django.shortcuts import render, redirect
from .models import Post, Category
from .forms import PostForm, CategoryForm
def create_post(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
form.save()
return redirect('post_list') # Перенаправляємо на список постів
else:
form = PostForm()
return render(request, 'blog/create_post.html', {'form': form})
def create_category(request):
if request.method == 'POST':
form = CategoryForm(request.POST)
if form.is_valid():
form.save()
return redirect('category_list') # Перенаправляємо на список категорій
else:
form = CategoryForm()
return render(request, 'blog/create_category.html', {'form': form})
У шаблонах це виглядатиме приблизно так:
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Зберегти</button>
</form>
Цей підхід зручний, але вимагає, щоб користувач спочатку створив категорії, а потім додав пости і вибрав відповідні категорії. Хочете більше зручності? Давайте подивимося, як зробити одну форму, яка буде підтримувати все.
Підхід №2: Inline форми для пов'язаних моделей
Коли потрібно дати користувачу можливість редагувати одразу і пост, і пов'язані категорії, на допомогу приходять inline форми. Django надає потужний інструмент під назвою inlineformset_factory.
Створення Inline FormSet
from django.forms import inlineformset_factory
from .models import Post, Category
CategoryInlineFormSet = inlineformset_factory(
Post, # Батьківська модель
Category, # Пов'язана модель
fields=('name',), # Поля, які редагуються
extra=1, # Кількість порожніх форм для нових об'єктів
can_delete=True # Можливість видаляти пов'язані об'єкти
)
Тут inlineformset_factory створює форму, яка дозволяє редагувати категорії, пов'язані з конкретним постом.
Представлення для роботи з Inline FormSet
Тепер потрібно написати представлення, щоб відобразити і обробити нашу inline форму.
from django.shortcuts import render, get_object_or_404, redirect
from .models import Post
from .forms import PostForm
from .forms import CategoryInlineFormSet
def edit_post(request, pk):
post = get_object_or_404(Post, pk=pk)
form = PostForm(instance=post)
formset = CategoryInlineFormSet(instance=post)
if request.method == 'POST':
form = PostForm(request.POST, instance=post)
formset = CategoryInlineFormSet(request.POST, instance=post)
if form.is_valid() and formset.is_valid():
form.save()
formset.save()
return redirect('post_list') # Перенаправляємо на список постів
return render(request, 'blog/edit_post.html', {'form': form, 'formset': formset})
Шаблон з основною формою та inline формами
Тепер створимо шаблон, де обидві форми будуть відображатися разом.
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<h3>Категорії:</h3>
{{ formset.management_form }}
{% for form in formset %}
{{ form.as_p }}
{% endfor %}
<button type="submit">Зберегти</button>
</form>
Як це працює?
PostFormдозволяє редагувати дані поста (title,content).CategoryInlineFormSetдозволяє редагувати категорії, пов'язані з постом.- Якщо користувач додає категорію або видаляє існуючу, це автоматично фіксується в базі.
Підхід №3: Використання віджетів для ManyToMany
Якщо тобі потрібно просте рішення для зв'язку ManyToMany, можна використовувати вбудовані віджети Django. Наприклад, віджет CheckboxSelectMultiple дозволяє відображати пов'язані об'єкти у вигляді списку чекбоксів.
Твоя форма буде виглядати так:
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content', 'categories']
widgets = {
'categories': forms.CheckboxSelectMultiple()
}
Тепер у шаблоні поле categories буде представлено списком чекбоксів, де користувач зможе вибрати декілька категорій.
Практичне завдання
- Реалізуй модель авторів
Author, пов'язаних із постами через полеForeignKey. - Створи форму, яка дозволить редагувати пости та пов'язані з ними дані про авторів.
- Налаштуй представлення та шаблон для роботи з цією формою.
Такий підхід дасть тобі реальне розуміння, як працюють форми для пов'язаних моделей і які з них зручніше використовувати в різних сценаріях.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ