В этом разделе мы рассмотрим создание древовидной модели категорий для постов с использованием MPTT. Категории будут связаны друг с другом как дочерние-родительские, категории верхнего уровня не будут иметь родительскую категорию.
MPTT - это метод хранения и обработки иерархических данных в базе данных. Библиотека django-mptt позволит создавать и работать с иерархиями. Проще говоря, мы сможем сделать древовидное представление категорий.
Перед началом работы установим django-mptt:
pip install django-mptt
И не забудем добавить его в INSTALLED_APPS файла settings.py:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'apps.blog.apps.BlogConfig',
'mptt'
]
Теперь перейдем к созданию самой модели Category:
from mptt.models import MPTTModel, TreeForeignKey
class Category(MPTTModel):
"""
Модель категорий с вложенностью
"""
title = models.CharField(max_length=255, verbose_name='Название категории')
slug = models.SlugField(max_length=255, verbose_name='URL категории', blank=True)
description = models.TextField(verbose_name='Описание категории', max_length=300)
parent = TreeForeignKey(
'self',
on_delete=models.CASCADE,
null=True,
blank=True,
db_index=True,
related_name='children',
verbose_name='Родительская категория'
)
class MPTTMeta:
"""
Сортировка по вложенности
"""
order_insertion_by = ('title',)
class Meta:
"""
Сортировка, название модели в админ панели, таблица в данными
"""
verbose_name = 'Категория'
verbose_name_plural = 'Категории'
db_table = 'app_categories'
def __str__(self):
"""
Возвращение заголовка категории
"""
return self.title
В поле создаётся объект класса TreeForeignKey из django-mptt, позволяющий делать древовидные категории. Аргумент self указывает, что внешний ключ ссылается на ту же модель, в которой он определен, то есть на Category.
Аргумент on_delete=models.CASCADE задает поведение при удалении связанного объекта. В данном случае, если родительская категория удалена, то все дочерние категории также будут удалены.
Аргументы null=True и blank=True позволяют полю parent быть необязательным и не требуют обязательного ввода значения при создании объекта.
Аргумент related_name='children' определяет имя обратной связи между родительской и дочерней категорией. То есть, у каждого объекта модели Category будет доступ к своим дочерним категориям через атрибут children.
Далее нам необходимо включить категории в модель статей, делается это следующим образом:
class Post(models.Model):
"""
Модель постов для нашего блога
"""
STATUS_OPTIONS = (
('published', 'Опубликовано'),
('draft', 'Черновик')
)
title = models.CharField(verbose_name='Название записи', max_length=255)
slug = models.SlugField(verbose_name='URL', max_length=255, blank=True, unique=True)
description = models.TextField(verbose_name='Краткое описание', max_length=500)
text = models.TextField(verbose_name='Полный текст записи')
category = TreeForeignKey('Category', on_delete=models.PROTECT, related_name='posts', verbose_name='Категория') # New
# Другие поля модели...
Мы должны использовать вместо отношения: ForeignKey импортированное отношение вложенности из модуля MPTT: TreeForeignKey
Регистрируем модель категорий в админке, и добавляем возможность скрывать категории по вложенности с помощью DraggableMPTTAdmin: До этого мы регистрировали уже модель Post.
from django.contrib import admin
from mptt.admin import DraggableMPTTAdmin
from .models import Category, Post
@admin.register(Category)
class CategoryAdmin(DraggableMPTTAdmin):
"""
Админ-панель модели категорий
"""
prepopulated_fields = {'slug': ('title',)}
admin.site.register(Post)
Отлично. Теперь нам необходимо провести миграции в базу данных:
python manage.py makemigrations
python manage.py migrate
Теперь давайте проверим работоспособность наших моделей в админ-панели, чтобы в нее войти нам нужно создать супер-пользователя, делается это следующей командой:
python manage.py createsuperuser
Далее переходим в админ-панель по следующему адресу: http://127.0.0.1:8000/admin/, авторизуемся, переходим в раздел категорий и попробуем создать первую категорию:
Далее добавляем вложенную категорию:
Добавим еще несколько категорий:
В списке категорий дочерние категории отображаются чуть сдвинутыми вправо относительно родителя.Также мы можем перемещать категории по иерархии. И раскрывать и скрывать список вложенных категорий.