Привет, Habr!

В этой статье мы научимся использовать Markdown вместе с Django на примере блога. Она написана для новичков, для базового ознакомления. Её в формате .md можно скачать в моём развивающемся Telegram канале.

Markdown — это простой язык разметки, используемый для создания форматированного текста (например, HTML) с помощью текстового редактора. Кстати, эту статью я писал, использую синтаксис Markdown)

Создание и активация виртуального окружения

python -m venv venv

source venv/bin/activate

Установка необходимых пакетов

pip install Django==4.2

Версия Django является LTS (версия с долгосрочной поддержкой) на момент 2024 года.

pip install markdown

Создание сайта и приложения

django-admin startproject markdown_blog

cd markdown_blog

python manage.py startapp posts

Мы создаем:

  • проект markdown_blog

  • приложение posts

Создание модели поста

Откройте файл posts/models.py, и напишите следующее:

from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse


class Post(models.Model):
    title = models.CharField(max_length=250)
    slug = models.SlugField(max_length=250)
    author = models.ForeignKey(User,
    on_delete=models.CASCADE,
    related_name='blog_posts')
    body = models.TextField()

	def __str__(self):
		return self.title

	def get_absolute_url(self):
        return reverse('post_detail',
        args=[self.id])

Мы создали модель Post, в которой есть такие поля:

  • title - Заголовок, длиной до 250 символов

  • slug - Слаг, длиной до 250 символов

  • author - ForeignKey к модели User. При удалении пользователя удаляются все его посты.

  • body - Текст поста (с этим полем мы и поработаем)

Добавьте приложение в markdown_blog/settings.py:

INSTALLED_APPS = [
				  # other apps,
				  'posts.apps.PostsConfig',
]

Создайте и выполните миграции:

python manage.py makemigrations

python manage.py migrate

Регистрация модели в админ панели

Откройте файл posts/admin.py , и напишите следующее:

from django.contrib import admin
from .models import Post

admin.site.register(Post)

Мы зарегистрировали модель Post в админ панели.

Создание представлений и шаблонов

Откройте файл posts/views.py, и напишите следующее:

from django.shortcuts import render, get_object_or_404

from .models import Post


def post_list(request):
    posts = Post.objects.all()
    return render(request,
    'post_list.html',
    {'posts': posts})

def post_detail(request, id):
	post = get_object_or_404(Post,
	id=id)
	return render(request,
	'post_detail.html',
	{'post': post})
	

Мы создали два простых представления:

  • post_list - Получает из базы данных список постов, и передаёт его в шаблон post_list.html.

  • post_detail - Получает из базы данных пост по id, и передает его в шаблон post_detail.html.

Создайте директорию posts/templates и внутри создайте новый файл post_list.html. В него напишите следующее:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>List of posts</title>
</head>

<body>
{% for post in posts %}
<h2>
	<a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
</h2>
<div>
	{{ post.body|truncatewords_html:30 }}
</div>
{% endfor %}
</body>
</html>

Внутри posts/templates создайте файл post_detail.html. В него напишите следующее:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Post: {{ post.title }}</title>
</head>

<body>
<a href='{% url "post_list" %}'>List of posts</a>
<h2>
	{{ post.title }}
</h2>
<div>
	{{ post.body }}
</div>
</body>
</html>

Откройте файл markdown_blog/urls.py, и напишите следующее:

from django.contrib import admin
from django.urls import path

from posts import views

urlpatterns = [
  path('admin/', admin.site.urls),
  path('', views.post_list, name='post_list'),
  path('<int:id>/', views.post_detail, name='post_detail'),
]

Мы создали два URL для наших представлений.

Добавление синтаксиса Markdown на сайт

Один из способов добавления синтаксиса Markdown на сайт - это создание конкретно-прикладного шаблонного фильтра.

Создайте пакет posts/templatetags, создайте файлы post_tags.py, и __init.py__. Файл post_tags.py будет содержать нашу библиотеку конкретно-прикладных шаблонных фильтров и тегов. В него напишите следующее:

from django import template
from django.utils.safestring import mark_safe

import markdown

register = template.Library()


@register.filter(name='markdown')
def markdown_format(text):
    return mark_safe(markdown.markdown(text))

Мы используем предоставляемую веб-фреймворком Django функцию
mark_safe, чтобы помечать результат как безопасный для прорисовки в шаблоне исходный код HTML. По умолчанию Django не будет доверять никакому исходному коду HTML и будет экранировать его перед его вставкой
в результат. Единственными исключениями являются переменные, которые
помечены как безопасные, чтобы тем самым избежать экранирования. Такое
поведение не дает Django выводить потенциально опасный исходный код
HTML и позволяет создавать исключения, дабы возвращать безопасный исходный код HTML.

Отредактируйте файл templates/post_detail.html:

{% load post_tags %}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Post: {{ post.title }}</title>
</head>

<body>
<a href='{% url "post_list" %}'>List of posts</a>
<h2>
	{{ post.title }}
</h2>
<div>
	{{ post.body|markdown }}
</div>
</body>
</html>

Отредактируйте файл templates/post_list.html:

{% load post_tags %}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>List of posts</title>
</head>

<body>
{% for post in posts %}
<h2>
	<a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
</h2>
<div>
	{{ post.body|markdown|truncatewords_html:30 }}
</div>
{% endfor %}
</body>
</html>

Проверка

Создайте суперпользователя:

python manage.py createsuperuser

Выполните все инструкции, и запустите сервер разработки:

python manage.py runserver

Пройдите по адресу 127.0.0.1:8000/admin, войдите, используя ранее введённые данные. Через админ панель добавьте несколько постов (используя синтаксис Markdown). Некоторые тексты есть ниже:

**Модель** — это единственный и точный источник информации о ваших данных. Он содержит основные поля и поведение данных, которые вы храните. Обычно каждая модель сопоставляется с одной таблицей базы данных.

### Основы
- - -

- Каждая модель представляет собой класс Python, который является подклассом `django.db.models.Model`.
- Каждый атрибут модели представляет поле базы данных.
- При всем этом Django предоставляет вам автоматически генерируемый API доступа к базе данных; см. [Создание запросов](https://docs.djangoproject.com/en/5.0/topics/db/queries/).


- - -
## MTV

Django подчиняется шаблону архитектурного дизайна **MTV** (Model-Template-View)

Обязанности в шаблоне архитектурного дизайна MTV Django распределе-
ны следующим образом:

- **Model** (модель) – определяет логическую структуру данных и является обработчиком данных между базой данных и их представлением

- **Template** (шаблон) – это слой представления. В Django используется система текстовых шаблонов, в которой хранится все, что браузер прорисовывает на страницах.

- **View** (представление) – взаимодействует с базой данных через модель и передает данные в шаблон для их прорисовки и просмотра.


- - -

Перейдите по адресу 127.0.0.1:8000. Вы должны увидеть список постов. Нажмите на одну из ссылок, вы увидите всё содержимое страницы.

Итог

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

P.S. это моя первая статья. Всем пока, Control + D!

Комментарии (9)


  1. Iscander_Che
    24.04.2024 13:47

    pip install Django==4.2

    Версия Django актуальна на момент 2024 года.

    Загадочная история. Буквально вчера поставил Django, и версия была 5.0.4. Другое дело, что 4.2 - это LTS...


    1. Mi11er
      24.04.2024 13:47

      Ну тут же пишут что она актуальная , а не последняя.

      4.2 действительно актуальна)


    1. VaRaS
      24.04.2024 13:47

      Актуальная <> последняя


      1. grigory_dl Автор
        24.04.2024 13:47

        Извините, LTS


    1. grigory_dl Автор
      24.04.2024 13:47

      Извиняюсь, невнимателен. Она LTS.


  1. zinoviev_tech
    24.04.2024 13:47

    Почини ссылку на ТГ — не работает


    1. grigory_dl Автор
      24.04.2024 13:47

      Починил, вот


  1. danilovmy
    24.04.2024 13:47
    +1

    Автор. Просьба. Удали фильтр. Ну пожалуйста. Сделай метод post.body_as_markdown.

    class Post(models.Model):
    
        def body_as_markdown(self):
            return mark_safe(markdown.markdown(self.body))  

    В шаблоне получаем:

    <div>
    	{{ post.body_as_markdown }}
    </div>

    Я опускаю, что можно просто виджет поменять у поля. Как вариант еще можно отдельно сделать класс рендеринга в markdown или в xml в зависимости от контекста.


    1. grigory_dl Автор
      24.04.2024 13:47

      Спасибо за обратную связь! Это моя первая статья, поэтому так получилось). В дальнейшем изменю её