В прошлом уроке мы установили Django MPTT и добавили в INSTALLED_APPS.
Создание вложенных (древовидных) категорий на Django
Возвращаемся в models.py и добавляем модель категории:
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
Пояснения:
- order_insertion_by - сортировка по вложенностям.
- Кстати, наследуемся мы уже MPTTModel
И конечно, теперь нам нужно добавить возможность указывать в статьях наши категории.
Поэтому к модели Article добавляем следующее:
class Article(models.Model):
title = models.CharField(verbose_name='Заголовок', max_length=255)
slug = models.SlugField(verbose_name='URL', max_length=255, blank=True)
category = TreeForeignKey('Category', on_delete=models.PROTECT, related_name='articles', verbose_name='Категория')
# другие поля...
Пояснения:
- Вместо обычной Foreign Key используем TreeForeignKey
Теперь давайте зарегистрируем нашу модель категорий, но с возможностью закрывать и открывать категории по вложенности прям в админке?
Регистрировал я вот так:
from mptt.admin import DraggableMPTTAdmin
@admin.register(Category)
class CategoryAdmin(DraggableMPTTAdmin):
"""
Админ-панель модели категорий
"""
list_display = ('tree_actions', 'indented_title', 'id', 'title', 'slug')
list_display_links = ('title', 'slug')
prepopulated_fields = {'slug': ('title',)}
fieldsets = (
('Основная информация', {'fields': ('title', 'slug', 'parent')}),
('Описание', {'fields': ('description',)}),
)
Пояснения:
- Регистрировать модели можно используя декоратор @admin.register(model)
- Чтобы видеть вложенность в админке, мы наследуемся от DraggableMPTTAdmin.
- list_display - это то, что мы видим в списке в админке, здесь есть tree_actions, indented_title - это специальные поля, от которых будет работать вложенность.
- list_display_links - кликабельные элементы из списка категорий
- prepopulated_fields - это авто заполнения с заголовка поля slug на латинице в виде url.
- fieldsets - здесь я упорядочил вид подробной категории в админке.
И так, давайте сделаем миграции и посмотрим результат в админ панели Django.
(venv) PS C:\Users\Razilator\Desktop\Courses\App\backend> python manage.py makemigrations
It is impossible to add a non-nullable field 'category' to article without specifying a default. This is because the database needs something to populate existing rows.
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit and manually define a default value in models.py.
Select an option: 1
Please enter the default value as valid Python.
The datetime and django.utils.timezone modules are available, so it is possible to provide e.g. timezone.now as a value.
Type 'exit' to exit this prompt
>>> None
Migrations for 'blog':
modules\blog\migrations\0002_category_article_category.py
- Create model Category
- Add field category to article
(venv) PS C:\Users\Razilator\Desktop\Courses\App\backend> python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
Applying blog.0002_category_article_category... OK
Как видите, у меня спросили, как я захотел исправить проблему с добавлением нового поля в модель Статьи. Я выбрал 1 вариант, и добавил значение None, т.е пусто. Заполнять мне ничего пока не надо. Но, лучше всегда добавляйте все заранее, чтоб потом не нужно было так исправлять.
Переходим в админку, смотрим результат:

Надеюсь, у вас все получилось. В следующем уроке я немного улучшу модели, добавлю наконец русский язык, как к лейблу модели, так и ко всему Django.