В прошлом уроке мы установили 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.

Комментарии к статье 0
Комментариев нет
Форма добавления комментария (необходима регистрация)