Django База [2023]: Автоматическое формирование slug, обработка кириллицы в Django #9
Django

Django База [2023]: Автоматическое формирование slug, обработка кириллицы в Django #9

Razilator

В данном уроке мы научимся работать со slug в Django 4.1, а именно форматировать их автоматически, а также обрабатывать кириллицу в slugField.

Что такое Slug?

slug - это тип поля в Django для создания человеко-понятных URL на латинице. С помощью slug мы можем автоматически конвертировать нашу запись, например с заголовком: Добро пожаловать на сайт. В url типа: dobro-pozhalovat-na-sajt.

Есть 2 способа конвертации заголовка в slug, но один работает лишь внутри административной модели, а другой работает всегда. Верно будет использовать оба варианта.

Настройка формирования slug в административной панели

Перейдем в файл admin.py в нашем приложении блог, и изменим код с этого:

blog/admin.py
from django.contrib import admin

from mptt.admin import DraggableMPTTAdmin
from .models import Category, Article

@admin.register(Category)
class CategoryAdmin(DraggableMPTTAdmin):
    """
    Админ-панель модели категорий
    """
    list_display = ('tree_actions', 'indented_title', 'id', 'title', 'slug')
    list_display_links = ('title', 'slug')
    prepopulated_fields = {'slug': ('title',)}

admin.site.register(Article)

На следующий:

blog/admin.py
from django.contrib import admin

from mptt.admin import DraggableMPTTAdmin
from .models import Category, Article

@admin.register(Category)
class CategoryAdmin(DraggableMPTTAdmin):
    """
    Админ-панель модели категорий
    """
    list_display = ('tree_actions', 'indented_title', 'id', 'title', 'slug')
    list_display_links = ('title', 'slug')
    prepopulated_fields = {'slug': ('title',)}


@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    prepopulated_fields = {'slug': ('title',)} 

Пояснение:

  • Мы добавили параметр prepopulated_fields, который позволяет с помощью JS обрабатывать заголовок в реальном времени, конвертирует даже кириллицу.

Смотрим результат в админ-панели:

При вводе любых символов заполняется поле URL
При вводе любых символов заполняется поле URL

Отлично, все работает. Теперь перейдем ко второму способу.

Добавление функции для сохранения уникального slug, обработка кириллицы с помощью python

Для обработки кириллицы в slug, нужно модернизировать функцию slugify(), для этого установим пакет pytils с помощью терминала: pip install pytils

Результат установки:

Терминал
(venv) PS C:\Users\Razilator\Desktop\Base\backend> pip install pytils
Collecting pytils
  Using cached pytils-0.4.1-py3-none-any.whl
Installing collected packages: pytils
Successfully installed pytils-0.4.1

[notice] A new release of pip available: 22.3 -> 22.3.1
[notice] To update, run: python.exe -m pip install --upgrade pip

Отлично, теперь я создам папку services в папке modules, а внутри два файла init.py и utils.py.

init.py необходим для инициализации папки как пакета Python.

В файл utils.py мы будем добавлять различные полезные функции для нашего проекта, поэтому мы ее отделим от приложения блог. Лично для меня это удобно, декомпозиция это всегда хорошо.

Теперь напишем функцию:

services/utils.py
from uuid import uuid4
from pytils.translit import slugify

def unique_slugify(instance, slug):
    """
    Генератор уникальных SLUG для моделей, в случае существования такого SLUG.
    """
    model = instance.__class__
    unique_slug = slugify(slug)
    while model.objects.filter(slug=unique_slug).exists():
        unique_slug = f'{unique_slug}-{uuid4().hex[:8]}'
    return unique_slug

Добавление services в INSTALLED_APPS

Далее необходимо добавить наш созданный модуль services в приложения конфигурации Django.

backend/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'modules.blog.apps.BlogConfig',
    'modules.services',
]

Применение уникальных авто-слагов в модели Django

Отлично, функцию написали и модуль добавили в установленные приложения, теперь функцию unique_slugify() необходимо применить в модели Article (Статей).

Для этого переходим в blog/models.py и добавим работу функции при сохранении экземпляра модели.

blog/models.py
from django.db import models
from django.core.validators import FileExtensionValidator
from django.contrib.auth import get_user_model
from django.urls import reverse

from mptt.models import MPTTModel, TreeForeignKey

from modules.services.utils import unique_slugify


class Article(models.Model):
    """
    Модель постов для сайта
    """    

    STATUS_OPTIONS = (
        ('published', 'Опубликовано'), 
        ('draft', 'Черновик')
    )

    title = models.CharField(verbose_name='Заголовок', max_length=255)
    slug = models.CharField(verbose_name='Альт.название', max_length=255, blank=True, unique=True)
    # Другие поля...
    
    # Другие функции...
    
    def __str__(self):
        return self.title
    
    def get_absolute_url(self):
        return reverse('articles_detail', kwargs={'slug': self.slug}) 
    
    def save(self, *args, **kwargs):
        """
        Сохранение полей модели при их отсутствии заполнения
        """
        if not self.slug:
            self.slug = unique_slugify(self, self.title)
        super().save(*args, **kwargs)

Пояснение:

Мы импортировали функцию по генерации slug, а также добавили метод save для обработки полей экземпляра. В условии, если нет slug, то мы генерируем slug из заголовка, а если такой slug существует, то мы добавляем символы uuid4.

Давайте протестируем нашу функцию:

Изменяем статью, поле url пустое
Изменяем статью, поле url пустое
Генерация slug при сохранении
Генерация slug при сохранении
Генерация slug при одинаковых заголовках статей в базе данных
Генерация slug при одинаковых заголовках статей в базе данных

Отлично, у нас все получилось!

;