Django 18. Представление для главной и категорий
Почти закончили с моделью категорий.
Осталось изменить представление главной страницы для вывода списка категорий, а так же добавить представление для страницы категории.
Откроем файл
В данный момент наше представление
В функции
Чуть ниже создадим переменную
Почти закончили с моделью категорий.
Осталось изменить представление главной страницы для вывода списка категорий, а так же добавить представление для страницы категории.
Откроем файл
views.py.В данный момент наше представление
index просто вывод заглушки. Оно ничего не передаёт в шаблон. Давайте это изменим.В функции
index, сделаем несколько отступов перед возвратом рендера. Там напишем переменную categories_list, в которой будем обращаться к нашей модели CategoryModel и получать все объекты через менеджера:categories_list = models.CategoryModel.objects.all()
Чуть ниже создадим переменную
context, это словарь, в котором ключом является имя переменной, к которой мы сможем обращаться внутри шаблона, а значением будут передаваемые данные. context = {
'categories_list': categories_list,
}Поскольку у нас теперь не
Напишем новую функцию
В шаблон небходимо передать информацию о текущей категории, для этого, создаём переменную
По аналогии создаём контекст и возврат рендера:
Готово. Теперь добавим URL-паттерн для страницы категорий. Откроем файл
Первым параметром мы формируем паттерн, состоящий из слова
Осталось зарегистрировать модель в панель адмниистратора. Откроем
Как можно заметить, мы наследуем наш класс не от
В кортеже
-
-
В переменной
Применим миграции:
Запустим Django и перейдём в панель администратора.
Там должна появиться наша модель. Сделайте несколько тестовых категорий разной вложенности, затем посмотрите как оно будет выглядеть на сайте.
Файлы к посту, можно получить в боте по коду: 234064
base.html выполняет функцию главной страницы, а index.html, ниже в строке с возвратом рендера изменим название файла и пропишем третьим параметром передачу в него контекста.return render(request,
'blog/index.html',
context)
Напишем новую функцию
category_page, принимающую в себя request, pk и slug.В шаблон небходимо передать информацию о текущей категории, для этого, создаём переменную
category и как и в предыдущей функции, получаем из модели данные. Только нам необходимы не все объекты, а конкретный.category = models.CategoryModel.objects.get(pk=pk)
По аналогии создаём контекст и возврат рендера:
context = {
'category': category,
}
return render(request,
'blog/category_page.html',
context)Готово. Теперь добавим URL-паттерн для страницы категорий. Откроем файл
urls.py в директории приложения и в список urlpatterns сайта, добавим следующую строку:path('category/<int:pk>-<str:slug>/', views.category_page, name='category_page'),Первым параметром мы формируем паттерн, состоящий из слова
category, pk категории и его slug-поля.Осталось зарегистрировать модель в панель адмниистратора. Откроем
admin.py и зарегистрируем:from mptt.admin import DraggableMPTTAdmin
@admin.register(models.CategoryModel)
class CategoryAdmin(DraggableMPTTAdmin):
list_display = ('tree_actions', 'indented_title', 'parent',)
list_display_links = ('indented_title',)
prepopulated_fields = {'slug': ('title',)}
search_fields = ['title', ]
Как можно заметить, мы наследуем наш класс не от
ModelAdmin, а от DraggableMPTTAdmin, данный класс позволяет организовать удобное отображение дерева категорий. В кортеже
list_display появились два не указанных в модели поля:-
tree_actions - добавляет в интерфейс функции взаимодействия с объектами.-
indented_title - поле заголовка с отступом.В переменной
prepopulated_fields мы прописываем, что поле slug будет заполняться автоматически на основе данных из поля title.Применим миграции:
python manage.py makemigrations
python manage.py migrate
Запустим Django и перейдём в панель администратора.
Там должна появиться наша модель. Сделайте несколько тестовых категорий разной вложенности, затем посмотрите как оно будет выглядеть на сайте.
Файлы к посту, можно получить в боте по коду: 234064
👍1
Django 19. Визуальный редактор CKEditor5
В прошлых постах сделали модель категорий. По хорошему теперь надо создать модель постов, но что бы не править модель в будущем, подготовим всё заранее.
Что бы посты выглядели прилично, с форматированием и рюшечками, установим визуальный редактор. Ранее, я использовал
Установим библиотеку
Не уходя из файла
В прошлых постах сделали модель категорий. По хорошему теперь надо создать модель постов, но что бы не править модель в будущем, подготовим всё заранее.
Что бы посты выглядели прилично, с форматированием и рюшечками, установим визуальный редактор. Ранее, я использовал
CKEditor, но в этот раз, решил опробовать CKEditor5. Установим библиотеку
pip install django-ckeditor-5. Вы уже должны были привыкнуть, что далее необходимо сразу добавить установленную библиотеку django-ckeditor-5==0.2.8 в requirements.txt. Также добавить django_ckeditor_5 в INSTALLED_APPS.Не уходя из файла
settings.py, в самый конец добавим конфигурацию:Код конфигурации ищите в файле settings.py в архиве с материалами к посту.
Переменная
Если вы используете тёмную тему панели администратора, то в директории
По умолчанию, редактор сохраняет изображения в корне директории
Создадим обработчик. Для этого перейдём в директорию приложения и создадим файл
В файле пропишем следующий код:
Настройки почти окончены, осталось прописать URL-паттерн. Перейдём в
Тем, кто читает данный пост, только, что бы узнать как установить редактор:
Для того, что бы форматированный в редакторе текст отображался на страницах сайта корректно, в шаблоне в тег вывода переменной с текстом, например,
Должно быть вот так:
В следующем посте начнём писать модель поста, будет много текста.
Файлы к посту, можно получить в боте по коду: 883614
CKEDITOR_5_CUSTOM_CSS задаёт пользовательские стили.Если вы используете тёмную тему панели администратора, то в директории
static необходимо создать директорию django_ckeditor_5 и в ней создать файл admin_dark_mode_fix.css со следующим содержимым:.ck.ck-editor {
color: black !important;
}По умолчанию, редактор сохраняет изображения в корне директории
media, что безусловно не удобно. Для того, что бы реализовать сохранение изображений в директориях post/%Y/%m/%d, служит параметр CKEDITOR_5_FILE_STORAGE. В нём определяется обработчик для сохранения файлов. Если у вас названия проекта совпадают, с моими, менять ничего не надо, если же не совпадают, то измените на соответствующие.Создадим обработчик. Для этого перейдём в директорию приложения и создадим файл
storage.py.В файле пропишем следующий код:
import os
from datetime import datetime
from urllib.parse import urljoin
from django.conf import settings
from django.core.files.storage import FileSystemStorage
class CustomStorage(FileSystemStorage):
"""Пользовательское хранилище для изображений."""
date = datetime.now().strftime('%Y/%m/%d')
location = os.path.join(settings.MEDIA_ROOT, f"post/{date}/")
base_url = urljoin(settings.MEDIA_URL, f"post/{date}/")
Настройки почти окончены, осталось прописать URL-паттерн. Перейдём в
urls.py в директории проекта и добавим в конец списка urlpatterns следующую строку:path("ckeditor5/", include('django_ckeditor_5.urls'), name="ck_editor_5_upload_file"),Тем, кто читает данный пост, только, что бы узнать как установить редактор:
Для того, что бы форматированный в редакторе текст отображался на страницах сайта корректно, в шаблоне в тег вывода переменной с текстом, например,
{{ post.body }} необходимо добавить фильтр safe. Должно быть вот так:
{{ post.body | safe }}.В следующем посте начнём писать модель поста, будет много текста.
Файлы к посту, можно получить в боте по коду: 883614
👍1
Приветствую.
Сайт для канала ещё в процессе разработки, но уже сейчас можно посмотреть, что получается.
Сайт: https://pressanybutton.ru/
Я уже говорил, что верстальщик из меня посредственный, поэтому, я буду рад вашим комментариям с предложениями, критикой или просто обсуждению сайта.
Сайт для канала ещё в процессе разработки, но уже сейчас можно посмотреть, что получается.
Сайт: https://pressanybutton.ru/
Я уже говорил, что верстальщик из меня посредственный, поэтому, я буду рад вашим комментариям с предложениями, критикой или просто обсуждению сайта.
🔥3👍2🤩1
Django 20. Модель поста
Сегодня напишем модель поста. Текста будет много и кода тоже.
Откроем файл
Создадим вложенный класс
Далее начнём описывать поля модели. У нас их будет 13 штук:
-
-
-
-
-
-
-
Сегодня напишем модель поста. Текста будет много и кода тоже.
Откроем файл
models.py и создадим новый класс PostModel унаследованный от models.Model.Создадим вложенный класс
Status унаследованный от models.TextChoices, в котором создадим два поля для статуса поста: Черновик и Опубликован.Далее начнём описывать поля модели. У нас их будет 13 штук:
-
title - поле с заголовком поста. Тип CharField.-
slug - поле преобразованное из заголовка в безопасный набор символов. Тип SlugField.-
image - поле для изображения поста. Тип ImageField.-
author - поле со ссылкой на пользователя, написавшего пост. Тип ForeignKey.-
category - поле со ссылкой на категорию. Тип TreeForeignKey.-
short_body - поле с коротким описанием/содержанием поста. Тип CKEditor5Field.-
full_body - поле с основным текстом поста. Тип CKEditor5Field.-
-
-
-
-
-
Далее вложенный класс
-
-
-
Метод
Методы
Dunder-метод
Код:
При желании, поле description в модели категории, так же можно изменить на CKEditor5Field, добавив в него графический редактор
publish - поле с датой публикации поста. Тип DateTimeField.-
created - поле с датой создания поста. Тип DateTimeField.-
updated - поле с датой обновления поста. Тип DateTimeField.-
status - поле со статусом поста. Выбор статуса происходит из класса Status. Тип CharField.-
views - поле со счётчиком просмотров. Тип IntegerField. Стоит отметить, что это не уникальные просмотры, а буквально каждое обновление страницы с постом, возможно в будущем, поле доработаем.-
objects - поле с определением менеджера для модели. Тип Manager.Далее вложенный класс
Meta со следующими полями:-
ordering - определяем сортировку по дате публикации в от самого нового.-
indexes - определяем индексирование базы данных по полю публикации.-
verbose_name и verbose_name_plural - определяем имя и множественное имя модели.Метод
get_absolute_url, возвращает URL-адрес для объекта модели.Методы
get_next_post и get_previous_post возвращают следующий и предыдущий пост в данной категории основываясь на дате публикации.Dunder-метод
__str__ возвращает заголовок поста.Код:
from django_ckeditor_5.fields import CKEditor5Field
from django.contrib.auth.models import User
from django.utils import timezone
class PostModel(models.Model):
"""Модель поста"""
class Status(models.TextChoices):
"""Класс выбора статуса поста"""
DRAFT = 'ЧЕ', 'Черновик'
PUBLISHED = 'ОП', 'Опубликовано'
title = models.CharField(max_length=200,
verbose_name="Заголовок")
slug = models.SlugField(verbose_name="Альт. заголовок")
image = models.ImageField(upload_to='post/%Y/%m/%d',
default='default/not_found.png',
verbose_name='Изображение поста')
author = models.ForeignKey(User,
on_delete=models.CASCADE,
related_name='post',
verbose_name="Автор")
category = TreeForeignKey('CategoryModel',
on_delete=models.PROTECT,
related_name='post',
verbose_name='Категория')
short_body = CKEditor5Field(max_length=350,
verbose_name="Краткое описание",
blank=True)
full_body = CKEditor5Field(verbose_name='Содержимое поста')
publish = models.DateTimeField(default=timezone.now,
verbose_name="Опубликовано")
created = models.DateTimeField(auto_now_add=True,
verbose_name="Создано")
updated = models.DateTimeField(auto_now=True,
verbose_name="Обновлено")
status = models.CharField(max_length=2,
choices=Status.choices,
default=Status.DRAFT,
verbose_name="Статус")
views = models.IntegerField(default=0,
verbose_name="Количество просмотров")
objects = models.Manager()
class Meta:
ordering = ['-publish']
indexes = [
models.Index(fields=['-publish']),
]
verbose_name = 'Пост'
verbose_name_plural = 'Посты'
def get_absolute_url(self):
"""Метод получения URL-адреса объекта"""
return reverse('blog:post_page', args=[int(self.pk), str(self.slug)])
def get_next_post(self):
"""Метод получения следующего поста"""
try:
return self.get_next_by_publish(category=self.category)
except PostModel.DoesNotExist:
return None
def get_previous_post(self):
"""Метод получения предыдущего поста"""
try:
return self.get_previous_by_publish(category=self.category)
except PostModel.DoesNotExist:
return None
def __str__(self):
return self.title
При желании, поле description в модели категории, так же можно изменить на CKEditor5Field, добавив в него графический редактор
Применим миграции:
Файлы к посту, можно получить в боте по коду: 398438
python manage.py makemigrationsПо аналогии с моделью категории нам далее необходимо: Зарегистрировать модель в панели администратора, написать представление для страницы поста, добавить URL-паттерн и сделать шаблон страницы с постом. Этим займёмся в следующих постах.
python manage.py migrate
Файлы к посту, можно получить в боте по коду: 398438
Django 21. Регистрация модели поста
Модель создали, теперь необходимо выполнить ещё ряд действий.
Начнём с регистрации модели в панели администратора. Перейдём в файл
Далее создаём 7 полей:
-
-
-
-
Модель создали, теперь необходимо выполнить ещё ряд действий.
Начнём с регистрации модели в панели администратора. Перейдём в файл
admin.py и создадим класс PostAdmin унаследованный от admin.ModelAdmin. Так же не забудьте про декоратор над классом или строку с регистрацией класса в самом низу файла, в зависимости от того, каким способом вы регистрируете модель.Далее создаём 7 полей:
-
list_display - Список имен полей модели, которые должны быть отображены в списке объектов.-
list_filter - Список имен полей, по которым Django создаст фильтры в правой части страницы изменения списка.-
search_fields - Активирует поиск в правом верхнем углу страницы со списком объектов.-
prepopulated_fields - Словарь, определяющий поля, которые должны быть автоматически заполнены из значений других полей.-
-
-
Поскольку по умолчанию, при создании нового объекта, в нашем случае нового поста, поле Автор будет представлять из себя выпадающий список со всеми зарегистрированными пользователями. Нам это ни к чему. Мы скрываем поле
Для того, чтобы пользователь подставлялся в поле автоматически, мы переопределяем метод сохранения объекта.
Создадим метод
В теле метода, мы в поле
Для того, что бы в столбцы объектов добавить отображение превью поста, создадим метод
В теле метода проверяем, есть ли изображение у объекта поста или нет. Если его нет, то вернём строку
Обратите внимание! Pycharm будет ругаться и просить сделать метод статическим, т.к. он не использует self. Игнорируем эту ошибку, поскольку нам необходимо, что бы метод знал, в каком классе он вызван, иначе он будет работать не корректно или вовсе не будет работать, если его сделать статическим.
И ниже, после метода добавим переопределение названия нового поля
Код:
Файлы к посту, можно получить в боте по коду: 810542
date_hierarchy - Поля DateField или DateTimeField в модели, которые должны быть использованы навигации по датам.-
ordering - Указывает порядок расположения записей на странице.-
exclude - Список полей, которые будут исключены из формы создания/редактирования объекта.Поскольку по умолчанию, при создании нового объекта, в нашем случае нового поста, поле Автор будет представлять из себя выпадающий список со всеми зарегистрированными пользователями. Нам это ни к чему. Мы скрываем поле
author со страницы создания/редактирования поста добавив поле в список exclude. Для того, чтобы пользователь подставлялся в поле автоматически, мы переопределяем метод сохранения объекта.
Создадим метод
save_model, принимающий ряд аргументов.В теле метода, мы в поле
author у будущего поста подставляем текущего пользователя. Сохраняем объект и вызовем родительский метод для сохранения модели.Для того, что бы в столбцы объектов добавить отображение превью поста, создадим метод
image_preview передав в него self и obj.В теле метода проверяем, есть ли изображение у объекта поста или нет. Если его нет, то вернём строку
No image found. Если изображение есть(а у нас оно всегда будет, но дополнительная защита всё же не повредит), то мы с помощью функции format_html, возвращаем тег img с путём до изображения.Обратите внимание! Pycharm будет ругаться и просить сделать метод статическим, т.к. он не использует self. Игнорируем эту ошибку, поскольку нам необходимо, что бы метод знал, в каком классе он вызван, иначе он будет работать не корректно или вовсе не будет работать, если его сделать статическим.
И ниже, после метода добавим переопределение названия нового поля
image_preview.Код:
from django.utils.html import format_htmlПерейдём в файл
@admin.register(models.PostModel)
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'image_preview', 'author', 'category', 'publish', 'created',
'updated', 'views', 'status', ]
list_filter = ['status', 'publish', 'author', ]
search_fields = ['title', 'body', ]
prepopulated_fields = {'slug': ('title',)}
date_hierarchy = 'publish'
ordering = ['status', 'publish', ]
exclude = ["author", 'views', ]
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
super().save_model(request, obj, form, change)
def image_preview(self, obj):
if obj.image:
return format_html(f'<img src="{obj.image.url}" width="100"/>')
else:
return '(No image found)'
image_preview.short_description = 'Превью'
urls.py в директории приложения и в паттерны сайта, добавим для страницы поста, следующую строку:path('post/<int:pk>-<str:slug>/', views.post_page, name='post_page'),
Не обращаем внимания на ошибку, у нас же всё ещё нет представления, этим займёмся в следующем посте.Файлы к посту, можно получить в боте по коду: 810542
👍1
Приветствую.
Небольшое дополнение к предыдущему посту(пост уже обновлён).
Как заметил Кирилл в чате, просто сохранять объект в переопределённом методе, без вызова родительского класса - не самая хорошая практика.
Я обновил пост, но, если вы уже повторили его, тогда в метод
Если у вас есть комментарии, вопросы или просто хотите пообщаться - заходите к нам в чат: https://t.iss.one/+cm-ITbA-JTczMTRi
Небольшое дополнение к предыдущему посту(пост уже обновлён).
Как заметил Кирилл в чате, просто сохранять объект в переопределённом методе, без вызова родительского класса - не самая хорошая практика.
Я обновил пост, но, если вы уже повторили его, тогда в метод
save_model добавьте строку super().save_model(request, obj, form, change).Если у вас есть комментарии, вопросы или просто хотите пообщаться - заходите к нам в чат: https://t.iss.one/+cm-ITbA-JTczMTRi
Telegram
Кот на салфетке
Чат для канала https://t.iss.one/press_any_button
Обсуждаем посты, программирование и просто болтаем.
Обсуждаем посты, программирование и просто болтаем.
👍1
Django 22. Представление для страницы поста
Сегодня мы напишем представление для страницы поста, а также добавим отображение постов на странице категории.
Начнём с представления поста. Перейдём в файл
В теле функции создадим переменную
Создаём переменную
Код:
Сегодня мы напишем представление для страницы поста, а также добавим отображение постов на странице категории.
Начнём с представления поста. Перейдём в файл
views.py и напишем функцию post_page, передавая те же аргументы, что и для представления категорий, а именно request, pk и slug.В теле функции создадим переменную
post и так же как мы делали для категории, получим из модели конкретный пост.Создаём переменную
context с переменной post и делаем возврат рендера.Код:
def post_page(request, pk, slug):
post = models.PostModel.objects.get(pk=pk)
post.views += 1
post.save()
context = {
'post': post,
}
return render(request,
'blog/post_page.html',
context)
Теперь изменим функцию
Под переменной
И тут есть два варианта вывода постов в категории.
1. Если нам нужно выводить посты строго в той категории, для которой они прописаны, то прописываем получение постов относящихся только к этой категории:
2. Если нам надо, что бы посты отображались как в предназначающейся категории, так и в категории выше, как пример у меня категория
Представления написаны, перейдём к шаблонам.
Сперва изменим шаблон страницы категории. Откроем файл
Под секцией с выводом информации о категории, напишем новую секцию, в которой будем через цикл
Код:
Подключим базовый шаблон, пропишем блок с контентом и внутри вывод информации о посте.
Код:
Затем перейдите на сайт и проверьте как всё работает.
Файлы к посту, можно получить в боте по коду: 220049
category_page.Под переменной
category, создаём ещё одну с именем posts.И тут есть два варианта вывода постов в категории.
1. Если нам нужно выводить посты строго в той категории, для которой они прописаны, то прописываем получение постов относящихся только к этой категории:
posts = models.PostModel.objects.filter(category=category).2. Если нам надо, что бы посты отображались как в предназначающейся категории, так и в категории выше, как пример у меня категория
Гайды и в ней категория Django. То создаём две переменные. В одной мы получаем список категорий, а во второй получаем посты относящиеся к категориям из списка:descendant_categories = category.get_descendants(include_self=True)Код:
posts = models.PostModel.objects.filter(category__in=descendant_categories)
# Первый вариантДля себя, выбрал второй вариант.
posts = models.PostModel.objects.filter(category=category)
# Второй вариант
descendant_categories = category.get_descendants(include_self=True)
posts = models.PostModel.objects.filter(category__in=descendant_categories)
context = {
'category': category,
'posts': posts,
}
Представления написаны, перейдём к шаблонам.
Сперва изменим шаблон страницы категории. Откроем файл
category_page.html в директории с шаблонами.Под секцией с выводом информации о категории, напишем новую секцию, в которой будем через цикл
for выводить элементы.Код:
<section class="posts">Наконец, создадим файл
<div class="container">
<hr>
{% for post in posts %}
<h2><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h2>
<p>{{ post.short_body | safe }}</p>
<hr>
{% endfor %}
</div>
</section>
post_page.html.Подключим базовый шаблон, пропишем блок с контентом и внутри вывод информации о посте.
Код:
{% extends "blog/base.html" %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
<section class="post">
<div class="container">
<h1>{{ post.title }}</h1>
<p>{{ post.full_body | safe }}</p>
</div>
</section>
{% endblock %}
Осталось всё проверить. Запустите Django, перейдите в панель администратора и добавьте несколько тестовых постов.Затем перейдите на сайт и проверьте как всё работает.
Файлы к посту, можно получить в боте по коду: 220049
👍2
Django 23. Добавляем sitemap и счётчики
Как и для любого сайта, аналитика посещений очень важна. Как и индексирование сайта в поисковых системах.
В этом посте опишу, как удобно встроить счётчики Яндекс.Метрики и Google Analytics в шаблон сайта, как создать файлы
Начнём с аналитики и счётчиков.
Дабы не засорять код базового шаблона длинными скриптами со счётчиками Яндекса и Google, установим пакет
В этом же файле пропишите ваши номера счётчиков:
Все доступные виды счётчиков, можно найти в официальной документации: https://django-analytical.readthedocs.io/en/latest/
Как и для любого сайта, аналитика посещений очень важна. Как и индексирование сайта в поисковых системах.
В этом посте опишу, как удобно встроить счётчики Яндекс.Метрики и Google Analytics в шаблон сайта, как создать файлы
sitemap.xml и robots.txt.Начнём с аналитики и счётчиков.
Дабы не засорять код базового шаблона длинными скриптами со счётчиками Яндекса и Google, установим пакет
pip install django-analytical. Пропишем его в requirements.txt и добавим analytical в INSTALLED_APPS.В этом же файле пропишите ваши номера счётчиков:
# Статистика
YANDEX_METRICA_COUNTER_ID = ''
GOOGLE_ANALYTICS_GTAG_PROPERTY_ID = ''
Все доступные виды счётчиков, можно найти в официальной документации: https://django-analytical.readthedocs.io/en/latest/
Затем перейдём в файл
В самом верху файла, после тэга подключения статики, добавьте тег
Затем, в блоке
Теперь займёмся настройкой файла
Перейдём в файл
Модуль
Выполним миграции
Запустите Django и перейдите в панель администратора. Там будет новый пункт меню
Откройте его. В списке увидите сайт
На открывшейся странице введите адрес вашего сайта, если он всё ещё на локальной машине, то введите
Cоздадим класс
В теле класса пропишем пять методов(для класса категории, их будет четыре):
-
-
-
-
-
Код:
Перейдём в файл
Создадим словарь, в котором перечислим наши классы из файла
Первое, что необходимо сделать - это создать сам файл. Перейдём в директорию
В нём пропишите необходимые разрешения или запреты для поисковых роботов, например, самый простой вариант:
Откроем файл
Файлы к посту, можно получить в боте по коду: 276141
base.html. Нам необходимо вставить теги аналитики.В самом верху файла, после тэга подключения статики, добавьте тег
{% load analytical %}.Затем, в блоке
<head> добавьте два тега. Один в самое начало блока, второй в самый конец:{% analytical_head_top %}
{% analytical_head_bottom %}
Точно также и со следующими двумя тегами, но уже в блок <body>:{% analytical_body_top %}
{% analytical_body_bottom %}
На этом всё. В скором времени, на странице счётчика начнёт появляться статистика.Теперь займёмся настройкой файла
sitemap.xml.Перейдём в файл
settings.py и в INSTALLED_APPS добавим следующие модули:'django.contrib.sites',И добавим переменную
'django.contrib.sitemaps',
SITE_ID = 1 в начало файла, после переменной BASE_DIR.Модуль
django.contrib.sites добавляет поддержку нескольких сайтов, это нам не нужно, однако это необходимо модулю django.contrib.sitemaps.Выполним миграции
python manage.py migrate.Запустите Django и перейдите в панель администратора. Там будет новый пункт меню
Сайты.Откройте его. В списке увидите сайт
example.com. Нажмите на него, что бы перейти к редактированию.На открывшейся странице введите адрес вашего сайта, если он всё ещё на локальной машине, то введите
127.0.0.1
В директории приложения создадим файл sitemap.py.Cоздадим класс
PostSitemap унаследованный от Sitemap.В теле класса пропишем пять методов(для класса категории, их будет четыре):
-
items - метод, возвращающий все объекты модели. В нашем случае, все опубликованные посты-
lastmod - метод, возвращающий дату обновления поста. Данный метод не нужен для класса с категориями, поскольку у нас нет поля изменения категории-
priority - метод, возвращающий приоритет для объектов модели. Приоритет определяется от 0.0(наименьший), до 1.0(наивысший).-
changefreq - метод, возвращающий периодичность обновления постов. Доступные варианты: "always", "hourly", "daily", "weekly", "monthly", "yearly", "never".-
location - метод, возвращающий URL-адрес объекта. Обратите внимание на то, что он заполняется также, как и метод get_absolute_url.Код:
from django.contrib.sitemaps import SitemapПо аналогии создайте классы для других моделей, которые необходимо внести в файл
from django.urls import reverse
from . import models
class PostSitemap(Sitemap):
def items(self):
return models.PostModel.objects.filter(status=models.PostModel.Status.PUBLISHED)
def lastmod(self, obj):
return obj.updated
def priority(self, obj):
return 0.8
def changefreq(self, obj):
return "weekly"
def location(self, obj):
return reverse('blog:post_page', args=[obj.pk, obj.slug])
sitemap.xml, например, модель категории.Перейдём в файл
urls.py в директории проекта.Создадим словарь, в котором перечислим наши классы из файла
sitemap.py и в конец urlpatterns добавим строку для доступа к файлу:from django.contrib.sitemaps.views import sitemapДля создания файла
from blog.sitemap import PostSitemap, CategorySitemap
sitemaps = {
'PostSitemap': PostSitemap,
'CategorySitemap': CategorySitemap,
}
urlpatterns = [
# ...
path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
]
robots.txt, достаточно сделать всего два действия.Первое, что необходимо сделать - это создать сам файл. Перейдём в директорию
static и в корне создадим файл robots.txt.В нём пропишите необходимые разрешения или запреты для поисковых роботов, например, самый простой вариант:
User-agent: *Второе, что необходимо сделать - это прописать URL-паттерн для доступа к файлу.
Disallow: /admin/
Откроем файл
urls.py в директории проекта и в конец urlpatterns добавим следующую строку:from django.urls import path, include, re_pathНа этом всё. Осталось добавить файлы в отслеживание поисковых систем.
from django.views.static import serve
re_path(r'^robots.txt$', serve, {'document_root': settings.STATIC_ROOT, 'path': "robots.txt"}),
Файлы к посту, можно получить в боте по коду: 276141
👍1
Django 24. Связь модели файла и поста
У нас есть модель для хранения файлов и модель поста.
Для того, чтобы у поста, можно было выбирать файл, а у файла был доступ к полям поста, их нужно связать.
Внешний ключ.
Откроем файл
Создадим переменную
В параметрах укажем:
- Модель файла.
- Действия при удалении файла. Если удалим файл, пост не будет удалён, значение поля обнулится.
- Разрешение быть пустым и не обязательным полем.
- Название поля.
- "Зависимое" имя поля.
У нас есть модель для хранения файлов и модель поста.
Для того, чтобы у поста, можно было выбирать файл, а у файла был доступ к полям поста, их нужно связать.
Внешний ключ.
Откроем файл
models.py и пропишем внешний ключ в модели поста PostModel к модели файла.Создадим переменную
file, сделав её полем Один-к-Одному(OneToOneField). В параметрах укажем:
- Модель файла.
- Действия при удалении файла. Если удалим файл, пост не будет удалён, значение поля обнулится.
- Разрешение быть пустым и не обязательным полем.
- Название поля.
- "Зависимое" имя поля.
file = models.OneToOneField(PostFilesModel,Поле
on_delete=models.SET_NULL,
null=True,
blank=True,
verbose_name="Файл",
related_name="post")
related_name позволит обращаться из объекта файла к связанному объекту поста.Генерация кода для файла.
Отвлечёмся от основной темы поста.
Не уходя из файла
В классе
Перейдём в файл
Применим миграции:
Возвращаемся к основной теме поста.
Для того чтобы отобразить ссылку на файл, в шаблоне необходимо обращаться к полю файл в модели файла и запрашивать его путь.
Изменим сериализатор, чтобы он возвращал не только название файла и путь до него, но и поля из поста, например, ссылку на пост.
Добавим два новых поля:
-
-
И метод, получающий URL поста, и добавляющий к нему
В методе используем
Полный код сериализатора:
Файлы к посту, можно получить в боте по коду: 187343
Отвлечёмся от основной темы поста.
Не уходя из файла
models.py добавим генерацию кода для файла.В классе
PostFilesModel создадим метод генерации уникального кода и переопределим метод сохранения модели. Должно получиться так:import randomНе обязательно, но поле
def save(self, *args, **kwargs):
if not self.code:
self.code = self.generate_unique_code()
super().save(*args, **kwargs)
@staticmethod
def generate_unique_code():
code = random.randint(100000, 999999)
while PostFilesModel.objects.filter(code=code).exists():
code = random.randint(100000, 999999)
return code
code можно сделать полем "только для чтения".Перейдём в файл
admin.py и найдём класс PostFilesAdmin. Добавим в него всего одно поле: readonly_fields = ('code',)
Теперь код будет генерироваться при создании объекта, а в панели администратора нельзя будет его изменить.Применим миграции:
python manage.py makemigrationsДобавление в шаблон.
python manage.py migrate
Возвращаемся к основной теме поста.
Для того чтобы отобразить ссылку на файл, в шаблоне необходимо обращаться к полю файл в модели файла и запрашивать его путь.
<h2 class="mb-3">Дополнительные материалы</h2>Изменение сериализатора.
<a href="{{ post.file.file.path }}" class="mb-1">Скачать материалы и исходный код с сайта.</a>
<p class="mt-1">Или в <a href="https://t.iss.one/press_any_button_bot">Telegram-боте</a> по коду: <b>{{ post.file.code }}</b></p>
Изменим сериализатор, чтобы он возвращал не только название файла и путь до него, но и поля из поста, например, ссылку на пост.
Добавим два новых поля:
-
post_url - возвращающий ссылку на пост, для этого задействуем метод SerializerMethodField, поскольку получение полного URL до поста, это метод get_absolute_url. Параметр read_only=True позволяет, в случае если у файла нет связанного поста, получить None вместо исключения.-
post_telegram_link - возвращающий ссылку на пост в Telegram.И метод, получающий URL поста, и добавляющий к нему
UTM-метку для сбора статистики переходов.В методе используем
try-except, также чтобы избежать исключения, если у файла нет связанного поста. Полный код сериализатора:
class FileModelSerializer(serializers.ModelSerializer):Следующим постом внесём изменения в наш бот.
file_path = serializers.SerializerMethodField()
post_url = serializers.SerializerMethodField()
post_telegram_link = serializers.StringRelatedField(source='post.telegram_link', read_only=True)
class Meta:
model = models.PostFilesModel
fields = ['title', 'file_path', 'post_url', 'post_telegram_link']
@staticmethod
def get_file_path(obj):
return obj.file.path if obj.file else None
@staticmethod
def get_post_url(obj: models.PostFilesModel):
try:
url = obj.post.get_absolute_url()
utm_params = '?utm_source=telegram&utm_medium=bot&utm_campaign=get_file'
return get_current_site(None).domain + url + utm_params
except ObjectDoesNotExist:
return None
Файлы к посту, можно получить в боте по коду: 187343
❤1👍1
AIOgram3 11. Обновление для команды get_file
В прошлом посте, мы добавили связь между файлом и постом на сайте и добавили в передачу дополнительные данные.
Давайте обновим команду
Обновление /get_file.
Откроем файл
В функции
Находим строку, в которой отправляется файл -
В аргументах у нас указан всего один параметр, который передаётся в сообщение пользователю -
Давайте его расширим, добавив
Сообщение пользователю.
Теперь откроем файл
В аргументы функции добавим новые -
В прошлом посте, мы добавили связь между файлом и постом на сайте и добавили в передачу дополнительные данные.
Давайте обновим команду
/get_file.Обновление /get_file.
Откроем файл
send_file.py.В функции
send_file_get_data нам нужно добавить дополнительные поля, которые мы получаем по API.Находим строку, в которой отправляется файл -
bot.send_document. В аргументах у нас указан всего один параметр, который передаётся в сообщение пользователю -
caption=views.file_caption(data['title']).Давайте его расширим, добавив
post_url и post_telegram_link:await bot.send_document(message.chat.id, document=file, caption=views.file_caption(
data['title'],
data['post_url'],
data['post_telegram_link']
))
Сообщение пользователю.
Теперь откроем файл
views.py и найдём функцию file_caption.В аргументы функции добавим новые -
post_url и post_telegram_link.В теле функции сделаем проверку, если
Если же данных не будет, то возвращаем просто строку с заголовком файла.
Форматирование сообщений.
Ссылки можно отправлять и напрямую, но тогда ссылка будет занимать много места.
Telegram предоставляет три вида форматирования текста:
Я остановился на
Чтобы добавить поддержку
Найдём переменную
В параметры к классу Bot, сразу после передачи токена, добавим параметр
Чтобы проверить работу запустите бота и Django. В панели администратора Django откройте объект поста, в поле файл выберите имеющийся файл и сохраните.
Затем в боте запросите этот файл.
В сообщении к файлу, кроме заголовка, будут находиться ссылки на пост.
Файлы к посту, можно получить в боте по коду: 213307
post_url содержит данные, то мы возвращаем сообщение пользователю, в котором будет заголовок файла и две ссылки - на сайт и на канал.Если же данных не будет, то возвращаем просто строку с заголовком файла.
def file_caption(file_title, post_url, post_telegram_link):
if post_url:
return f'Файл к посту: {file_title}\nПост на сайте: <a href="{post_url}">pressanybutton.ru</a>\nПост на канале: <a href="{post_telegram_link}">Код на салфетке</a>'
return f'Файл к посту: {file_title}'
Форматирование сообщений.
Ссылки можно отправлять и напрямую, но тогда ссылка будет занимать много места.
Telegram предоставляет три вида форматирования текста:
markdown, markdownv2 и HTML.Я остановился на
HTML.Чтобы добавить поддержку
HTML-разметки в сообщениях, перейдём в файл settings.py.Найдём переменную
bot. В параметры к классу Bot, сразу после передачи токена, добавим параметр
parse_mode='HTML'.bot = Bot(token=Secrets.token, parse_mode='HTML')
Чтобы проверить работу запустите бота и Django. В панели администратора Django откройте объект поста, в поле файл выберите имеющийся файл и сохраните.
Затем в боте запросите этот файл.
В сообщении к файлу, кроме заголовка, будут находиться ссылки на пост.
Файлы к посту, можно получить в боте по коду: 213307
👍1
Приветствую!
В длинных постах можно запутаться поэтому, собираю воедино всё, что есть на данный момент.
Оглавления
Для удобства навигации есть посты с оглавлениями по темам:
Список всех постов по Django
Список всех постов по AIOgram3
Список всех постов по Docker
Список всех постов по Полезным инструментам
Чат
У канала есть чат. Присоединяйтесь!
Бот и сайт
У канала также есть бот с материалами к постам
И сайт(в процессе наполнения вышедшими постами)
Поддержка
Если вам нравится канал и выходящий материал, поделитесь ссылкой с людьми, кому это тоже может быть интересно.
В длинных постах можно запутаться поэтому, собираю воедино всё, что есть на данный момент.
Оглавления
Для удобства навигации есть посты с оглавлениями по темам:
Список всех постов по Django
Список всех постов по AIOgram3
Список всех постов по Docker
Список всех постов по Полезным инструментам
Чат
У канала есть чат. Присоединяйтесь!
Бот и сайт
У канала также есть бот с материалами к постам
И сайт(в процессе наполнения вышедшими постами)
Поддержка
Если вам нравится канал и выходящий материал, поделитесь ссылкой с людьми, кому это тоже может быть интересно.
👍2🔥2👏1