Создание сайта на Django: Урок 18, создание абстрактной модели, наследование Django
avatar
7 | (offline)
❤️‍🔥Notehunter Developer
Добавлено:
Категория: Руководства «Django»
Комментариев: 0

Создание абстрактного класса модели

В модуле system я создам папку models, с __init__.py и файлом abstract.py, где и будет наша наследуемая абстрактная модель с SEO полями.

Структура:

  • modules
    • blog
    • system
      • models
        • __init__.py
        • abstract.py
      • migrations
      • services

Я добавлю следующее содержимое:

modules/blog/models/abstract.py

from django.db import models

class AbstractBaseMeta(models.Model):
    """
    Мета теги для обработки SEO в моделях, чтобы продвигать статьи в поисковике
    """
    meta_title = models.CharField(verbose_name='Мета-название', max_length=255, blank=True)
    meta_description = models.CharField(verbose_name='Мета-описание', blank=True, max_length=300)
    meta_keywords = models.CharField(verbose_name='Ключевые слова', max_length=255, blank=True)

    class Meta:
        abstract = True

Пояснения:

  • Мы добавляем свойство abstract = True, для того, чтобы мы могли наследоваться от этого класса, но не создавать его в базе данных, а создавать лишь для конкретной модели.

Если вы пользуетесь структурой как у меня, то в файл __init__.py добавим импорты:


from modules.system.models.abstract import AbstractBaseMeta

__all__ = (
    'AbstractBaseMeta',
   
)

Давайте теперь попробуем наследоваться в Article и Category от AbstractBaseModel?

modules/blog/models/articles.py

from modules.system.models import AbstractBaseMeta

class Article(AbstractBaseMeta):
    title = models.CharField(verbose_name='Заголовок', max_length=255)
    slug = models.SlugField(verbose_name='URL', max_length=255, blank=True)
    #some fields
    #some methods

modules/blog/models/categories.py

from modules.system.models import AbstractBaseMeta

class Category(MPTTModel, AbstractBaseMeta):
    title = models.CharField(max_length=255, verbose_name='Название категории')
    slug = models.SlugField(max_length=255, verbose_name='URL категории', blank=True)

Пояснения:

  • В статьях вместо models.Model мы наследуемся от AbstractBaseMeta, ибо та в свою очередь уже наследуется от models.Model.
  • В категориях мы добавляем уже к MPTTModel рядом AbstractBaseMeta, не нарушая целостность MPTT дерева.

Таким образом, абстрактные модели можно использовать не повторяя одни и те же поля для различных моделей. 

Давайте создадим и применим миграции:

(venv) PS C:\Users\Razilator\Desktop\Courses\App\backend> python manage.py makemigrations
Migrations for 'blog':
  modules\blog\migrations\0003_article_meta_description_article_meta_keywords_and_more.py
    - Add field meta_description to article
    - Add field meta_keywords to article
    - Add field meta_title to article
    - Add field meta_description to category
    - Add field meta_keywords to category
    - Add field meta_title to category
    - Alter field thumbnail on 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.0003_article_meta_description_article_meta_keywords_and_more... OK

Давайте взглянем на структуру таблиц статей и категорий в базе данных:

Статьи:

Категории:

Давайте ещё добавим в метод сохранения автоматическое их заполнение?

Но перед этим, мы добавим функцию получения повторяющихся слов для meta_keywords заполнения, найденную на просторах stackoverflow.

modules/system/services/utils.py

import collections
import io
import re
from django.utils.html import strip_tags

def get_meta_keywords(description):
    """
    Генератор meta-keywords из описания статьи
    """
    count_min = 3
    count_length = 5
    collection = collections.Counter()
    content = io.StringIO(description)
    keywords = []
    for line in content.readlines():
        collection.update(re.findall(r"[\w']+", strip_tags(line).lower()))
    for word, count in collection.most_common():
        if len(word) > (count_length - 1) and count > (count_min - 1):
            keywords.append(word)
    return ', '.join(map(str, keywords))

Примечание: этот код может вызывать ошибки, ибо наше поле ограничено 255 символами, а он может собрать слов очень много. Поэтому, лучше повысьте count_min до 7-8, также и count_length.

А теперь можно и работать с методом сохранения в статьях и категориях

modules/blog/models/articles.py

from django.template.defaultfilters import striptags, truncatewords_html
from modules.system.services.utils import ImageDirectorySave, unique_slugify, get_meta_keywords


class Article(AbstractBaseMeta):
    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='Категория')
    short_description = models.TextField(max_length=300, verbose_name='Краткое описание')
    full_description = models.TextField(verbose_name='Описание')
    
    #some fields
    #some methods
    
    def save(self, *args, **kwargs):
        """
        Сохранение полей модели при их отсутствии заполнения
        """
        if not self.slug:
            self.slug = unique_slugify(self, self.title)
        if not self.meta_title:
            self.meta_title = self.title
        if not self.meta_description:
            self.meta_description = striptags(truncatewords_html(self.short_description, 300))
        if not self.meta_keywords:
            self.meta_keywords = get_meta_keywords(self.full_description)
        super().save(*args, **kwargs)

Тоже самое для категорий:

modules/blog/models/categories.py

from django.db import models
from mptt.fields import TreeForeignKey
from mptt.models import MPTTModel

from django.template.defaultfilters import striptags, truncatewords_html

from modules.system.models import AbstractBaseMeta
from modules.system.services.utils import unique_slugify, get_meta_keywords



class Category(MPTTModel, AbstractBaseMeta):
    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:
        """
        Сортировка, название модели в админ панели, таблица в данными
        """
        unique_together = 'parent', 'slug'
        verbose_name = 'Категория'
        verbose_name_plural = 'Категории'
        db_table = 'app_categories'

    def save(self, *args, **kwargs):
        """
        Сохранение полей модели при их отсутствии заполнения
        """
        if not self.slug:
            self.slug = unique_slugify(self, self.title)
        if not self.meta_title:
            self.meta_title = self.title
        if not self.meta_description:
            self.meta_description = striptags(truncatewords_html(self.description, 300))
        if not self.meta_keywords:
            self.meta_keywords = get_meta_keywords(self.description)
        super().save(*args, **kwargs)

    def __str__(self):
        """
        Возвращение заголовка статьи
        """
        return self.title

Пояснения:

  • Если мы не заполнили мета-описание, то оно автоматически формируется из краткого содержания, обрезаются html элементы, а также содержание будет не более 300 символов.
  • Если мы не заполним мета-заголовок, то он автоматически заполнится из названия материала.
  • Если мы не заполним мета-ключевые-слова, то они сгенерируются из полного описания через нашу функцию.

А теперь давайте глянем на автоматизацию заполнения!

Отлично, после сохранения у нас все автоматически заполнилось! Но не забывайте, вы сами можете изменять эти поля как хотите. И в дальнейшем я покажу вам в уроках SEO применение данных полей на сайте!

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