Реализация похожих статей будет происходит в представлении ArticleDetailView:
modules/blog/views/articles.py
class ArticleDetailView(DetailView):
"""
Представление детальной статьи
"""
model = Article
template_name = 'modules/blog/articles/article-detail.html'
context_object_name = 'article'
queryset = Article.custom.detail()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = self.object.title
context['form'] = CommentCreateForm
return context
Модернизируем следующим образом:
modules/blog/views/articles.py
import random
from django.db.models import Count
class ArticleDetailView(DetailView):
"""
Контроллер детальной статьи
"""
model = Article
template_name = 'modules/blog/articles/article-detail.html'
context_object_name = 'article'
def get_queryset(self):
queryset = Article.custom.detail()
return queryset
def get_similar_articles(self, obj):
article_tags_ids = obj.tags.values_list('id', flat=True)
similar_articles = Article.objects.filter(tags__in=article_tags_ids).exclude(id=obj.id)
similar_articles = similar_articles.annotate(related_tags=Count('tags')).order_by('-related_tags')
similar_articles_list = [similar for similar in similar_articles.all()]
random.shuffle(similar_articles_list)
return similar_articles_list[0:6]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = f'{self.object.title}'
context['form'] = CommentCreateForm
context['similar_articles'] = self.get_similar_articles(self.object)
return context
Пояснения:
- Через функцию get_similar_articles мы получаем список тегов, далее мы добавляем фильтр к статьям, получая статьи по тегам из просматриваемой статьи, и исключая статью в которой мы находимся.
- Далее мы сортируем статьи по тегам, и все это рандомизируем.
- Возвращаем в контекст как выполненную функцию и выводим в шаблон
Я подправлю main.html для вывода похожих новостей в sidebar'е.
templates/main.html
<!DOCTYPE html>
<html lang="ru">
<head>
{% load static %}
<meta charset="UTF-8">
<title>{{ title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- INCLUDE CSS -->
<link href="{% static 'plugins/bootstrap-5.2.0-dist/css/bootstrap.min.css' %}" type="text/css" rel="stylesheet">
</head>
<body>
<div class="container">
{% include 'navbar.html' %}
<div class="row">
<div class="col-md-8">
{% include 'includes/messages.html' %}
{% block content %}
{% endblock %}
{% include 'pagination.html' %}
</div>
<div class="col-md-4">
{% block sidebar %}
{% endblock %}
</div>
</div>
</div>
<script src="{% static 'plugins/bootstrap-5.2.0-dist/js/bootstrap.bundle.min.js' %}"></script>
<script src="{% static 'custom/js/backend.js' %}"></script>
{% block script %}
{% endblock %}
</body>
</html>
Пояснения:
- Добавил {% block sidebar %} {% endblock %}
Теперь перейду в article-detail.html и добавлю в созданный блок похожие статьи
templates/modules/blog/articles/article-detail.html
{% extends 'main.html' %}
{% load static %}
{% block content %}
<div class="card border-0 mb-3">
<div class="card-body">
<div class="row">
<div class="col-md-4">
<figure>
<img src="{{ article.get_thumbnail }}" width="200" alt="{{ article.title }}">
</figure>
</div>
<div class="col-md-8">
<h5 class="card-title">
{{ article.title }}
</h5>
<small class="card-subtitle">
{{ article.created_at }} / {{ article.category }}
</small>
<div class="card-text">
{{ article.full_description }}
</div>
<hr/>
Добавил: <img src="{{ article.author.profile.get_avatar }}" class="rounded-circle" width="26" height="26"/> {{ article.author }}
<hr/>
<strong>Теги записи</strong>: {% for tag in article.tags.all %} <a href="{% url 'article-list-by-tags' tag.slug %}">{{ tag }}</a>, {% endfor %}
<hr/>
{% if article.reason %}{{ article.reason }} / обновил: {{ article.updated_by }}{% endif %}
</div>
</div>
</div>
</div>
<div class="card border-0">
<div class="card-body">
<h5 class="card-title">
Комментарии
</h5>
{% include 'modules/blog/comments/comments-list.html' %}
</div>
</div>
{% endblock %}
{% block sidebar %}
<div class="card mb-2 border-0">
<div class="card-body">
<div class="card-title">
Похожие статьи
</div>
<div class="card-text">
<ul class="similar-articles">
{% for sim_article in similar_articles %}
<li><a href="{{ sim_article.get_absolute_url }}">{{ sim_article.title }}</a></li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endblock %}
{% block script %}
<script src="{% static 'custom/js/article-detail.js' %}"></script>
{% endblock %}
Проверяем в шаблоне:

Отлично. Мы добавили похожие статьи как и на моем сайте notehunter.net!