Django - Tutorial 014. Displays a list of popular articles on Django

Many blogs and news sites to keep the audience's attention using such methods as ranking popular articles this week, related publications, and some major resources and more advice on user preferences.

First, it was decided to do - it displays a list of popular articles. The first version of the popular articles was based on a general counter views and eventually deduced the article with the most views. This is generally a bad option, since the TOP will result in articles just scored the highest number of views for all time.

Therefore it was necessary to change something. As a result, the output has been introduced popular articles in last 7 days in its simplest form. That is, the table has been added which introduced views of articles by day. Of course, the accuracy of counting under heavy load can vary greatly, but attendance is not reached the 5,000 - 10,000 unique visitors a day - it's not so important.

Now lets proceed with an example of how to make a list of popular articles Django .

Article and Statistics models

Here is a stripped-down version of the model for items, as in this case, I'm interested in only the title of the article.

class Article(models.Model):
    class Meta:
        db_table = "article"

    title = models.CharField('Title', max_length=200)

    def __str__(self):
        return self.title

A statistical model views on articles will be the next code.

class ArticleStatistic(models.Model):
    class Meta:
        db_table = "ArticleStatistic"

    article = models.ForeignKey(Article)                    
    date = models.DateField('Date', default=timezone.now)   
    views = models.IntegerField('Views', default=0)     

    def __str__(self):
        return self.article.title


class ArticleStatisticAdmin(admin.ModelAdmin):
    list_display = ('__str__', 'date', 'views')  
    search_fields = ('__str__', )                

In this code, made two models:

  1. The model itself to collect statistics
    Model to display the data in the admin area.

These models will be written in models.py file.

Do not forget to also register the model in the admin in admin.py file.

from django.contrib import admin

from .models import Article, ArticleStatistic, ArticleStatisticAdmin

admin.site.register(Article)
admin.site.register(ArticleStatistic, ArticleStatisticAdmin)

Then do not forget to do a database migration.

python manage.db makemigrations
python manage.db migrate

Views

Once we have added to the model, it remains to add the conclusion of popular articles and make the counting statistics when requested article from the server.

from django.views import View
from django.shortcuts import render_to_response, get_object_or_404
from django.utils import timezone
from django.db.models import Sum

# To work with the models of articles I have used knowledge module
from knowledge.models import Article, ArticleStatistic  


# while module is used for displaying articles post
# This was done in order to URL items was the following form
#    /post/42/ - where 42 - it is id od article in database
class EArticleView(View):
    template_name = 'knowledge/article.html'    

    def get(self, request, *args, **kwargs):
        article = get_object_or_404(Article, id=self.kwargs['article_id'])    
        context = {}

        # Then we pick up the object of today's statistics, or create a new one if required
        obj, created = ArticleStatistic.objects.get_or_create(
            defaults={
                "article": article,
                "date": timezone.now()
            },
            # At the same time define a fence or statistics object creation
            # by two fields: date and a foreign key to the article
            date=timezone.now(), article=article    
        )
        obj.views += 1    
        obj.save(update_fields=['views'])

        # Now pick up the list of the last 5 most popular articles of the week
        popular = ArticleStatistic.objects.filter(
            # filter records in the last 7 days
            date__range=[timezone.now() - timezone.timedelta(7), timezone.now()]
        ).values(
            # Taking the field of interest to us, namely, the id and the title
            # Unfortunately we can not to pick up an object on the foreign key in this case 
            # Only the specific field of the object
            'article_id', 'article__title'
        ).annotate(
            # Summing over rated recording
            # All sum properly with matching fields for the requested objects
            views=Sum('views')
        ).order_by(
            # sort the records Descending
            '-views')[:5]  # Take 5 last records

        context['popular_list'] = popular # We went to the context of the list of articles

        return render_to_response(template_name=self.template_name, context=context)

Display template

And now just be displayed in page template list of popular articles of the week. I'll note that only give you a template to display a list. This is enough to understand how to get a list of articles on the page.

Do not also forget that I am using django_bootstrap3, therefore, the pattern looks accordingly.

{% if popular_list %}{% load bootstrap3 %}
<ul class="list-group">
<li class="list-group-item active"><strong>Популярные публикации за неделю</strong></li>
{% for pop_article in popular_list %}
    <li class="list-group-item">
    <a href="{% url 'post:article' pop_article.article_id %}">{{ pop_article.article__title }}</a>
    </li>
{% endfor %}
</ul>
{% endif %}

URL pattern:

url(r'^(?P<article_id>[0-9]+)/$', views.EArticleView.as_view(), name='article'),

Result

The list of popular articles will be similar to that used on this site, but without the total number of views and, in the admin stats will look like this:

For Django I recommend VDS-server of Timeweb hoster .

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.
Support the author Donate
R
  • #
  • Jan. 5, 2017, 1:20 a.m.

Добрый день! В Django я новичок. По Вашей инструкции у меня получилось вывести популярные статьи, но они видны только в статье. Как вывести их на главную страницу? Заранее спасибо за ответ!

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

R

Спасибо Вам большое!!!

IM
Здравствуйте Евгений! Чему может быть причиной эта ошибка:
Reverse for 'post' with arguments '(1,)' not found. 1 pattern(s) tried: ['main\\/(?P<section>[^/]+)\\/(?P<post_id>[^/]+)\\/$']
Мой урл, и вьюха.
app_name = 'main'

urlpatterns = [
    path('', views.Index, name='index'),
    path('<section>/', views.SectionView.as_view(), name='section'),
    path('<section>/<post_id>/', views.PostView.as_view(), name='post')
]

class PostView(View):
    template_name = 'main/post.html'    # Шаблон статьи
 
    def get(self, request, *args, **kwargs):
        post = get_object_or_404(Post, id=self.kwargs['post_id'])
        context = {}
        obj, created = PostStatistic.objects.get_or_create(defaults={"post": post,"date": timezone.now()}, date=timezone.now(), post=post)
        obj.views += 1
        obj.save(update_fields=['views'])
 
        popular = PostStatistic.objects.filter(
            date__range=[timezone.now() - timezone.timedelta(7), timezone.now()]
        ).values(
            'post_id', 'post__title'
        ).annotate(
            views=Sum('views')
        ).order_by(
            '-views')[:2]

        context['post_id'] = post
        context['popular_list'] = popular
        context['username'] = auth.get_user(request).username
 
        return render_to_response(template_name=self.template_name, context=context)
Ну и шаблон до кучи:

{% if popular_list %}
<ul class="list-group">
<li class="list-group-item active"><strong>Популярные публикации за неделю</strong></li>
{% for pop_post in popular_list %}
    <li class="list-group-item">
    <a href="{% url 'main:post' pop_post.post_id %}">{{ pop_post.post__title }}</a>
    </li>
{% endfor %}
</ul>
{% endif %}
Очень надеюсь на вашу помощь.

Добрый день!

Во-первых перепишите urlpatterns так

urlpatterns = [
    path('', views.Index, name='index'),
    path('<section>/<post_id>/', views.PostView.as_view(), name='post'),
    path('<section>/', views.SectionView.as_view(), name='section')
]
Маршрутизатор url в Django иногда тупит из-за последовательности объявления шаблонов. Поэтому лучше шаблоны url прописывать в алфавитном порядке и больший шаблон ставить первым.

Во-вторых вы используете id, а там нужно тогда указать, что это у вас целочисленное значение, по умолчанию обрабатывается как строка. Значит результирующая запись будет такой.
urlpatterns = [
    path('', views.Index, name='index'),
    path('<section>/<int:post_id>/', views.PostView.as_view(), name='post'),
    path('<section>/', views.SectionView.as_view(), name='section')
]
IM

К сожалению это не изменило ситуацию. Убираю ссылку на статью из шаблона и все работает.  Вероятно не правильно сформирована ссылка в шаблоне.

Да. действительно, на это я не обратил внимания. Напишите либо так

<a href="{% url 'main:post' pop_post.id %}">{{ pop_post.post__title }}</a>
либо так
<a href="{% url 'main:post' pop_post.pk %}">{{ pop_post.post__title }}</a>
IM

И этот вариант к сожалению не рабочий =(

Извиняюсь, неправильно написал последний ответ. В рамках данной статьи это не правильно, у вас же во вьюшке формируется объект у которого в post_id должен содержаться его PrimaryKey.

 
Вообще можете показать модель PostStatistic? Я явно что-то упускаю здесь.
IM

Такая же как и у вас вроди бы.


class PostStatistic(models.Model):
    class Meta:
        db_table = "PostStatistic"
 
    post = models.ForeignKey(Post, on_delete=models.CASCADE) 
    date = models.DateField('Дата', default=timezone.now)   
    views = models.IntegerField('Просмотры', default=0)
 
    def __str__(self):
        return self.post.title

мда... глупо как-то получилось. У вас же в шаблоне два аргумента задано.

Один аргумент - это либо id раздела, либо его имя (вомзожно тоже самое slug поле, а вторым параметром идёт id самого поста).

Полагаю, что вы, возможно, основывались на вот этой статье , когда изучали материал.
Дело в том, что здесь идёт иной подход в формировании url для поста. И вам ещё потребуется указать раздел этого поста в шаблоне.

Что-то типо такого
<a href="{% url 'main:post' pop_post.post__section pop_post.post_id %}">{{ pop_post.post__title }}</a>

Думаю, что вам нужно немного поправить ещё вот этот метод
popular = PostStatistic.objects.filter(
    date__range=[timezone.now() - timezone.timedelta(7), timezone.now()]
).values(
    'post_id', 'post__title', 'post__section'
).annotate(
    views=Sum('views')
).order_by(
    '-views')[:2]
IM

Если делать таким образом, то вьюха отдает id категории а не slug. Попробую пост сделать отдельным приложением. Во всяком случае большое спасибо за помощь.

А. Так вы slug использовали? Тогда там надо брать поле slug из section в запросе.

Но реально, сделайте post лучше отдельным приложение.
Я по началу делал на сайте статьи с зависимостью от секции, но потом понял, что это будет ошибкой. Дело в том, что если у вас поменяется раздел у статьи или поста, то тогда пост будет располагаться по другому url, а старый будет отдавать ошибку 404. Это плохо для индексации сайта. Поэтому я вовремя опомнился и сделал url поста независимым от раздела.
IM

Вот и я пришел к тому же пути. Так вроди бы логично и красиво выглядит.

Comments

Only authorized users can post comments.
Please, Log in or Sign up
How to become an author?

Contribute to the evolution of the EVILEG community.

Learn how to become a site author.

Learn it
Donate

Good day, Dear Users!!!

I am Evgenii Legotckoi, developer of EVILEG. And it is my hobby project, which helps to learn programming another programmers and developers

If the site helped you, and you want also support the development of the site, than you can donate by following ways

PayPalYandex.Money
Timeweb

Let me recommend you the excellent hosting on which EVILEG is located.

For many years, Timeweb has been proving his stability.

For projects on Django I recommend VDS hosting

View Hosting Timeweb
AS
May 26, 2020, 11:29 a.m.
Artem Sun-Dun-Chan

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:50points,
  • Rating points-4
MN
May 25, 2020, 11:33 a.m.
Mitja Nagibin

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:50points,
  • Rating points-4
f
May 25, 2020, 5:05 a.m.
falcon

C++ - Test 001. The first program and data types

  • Result:66points,
  • Rating points-1
Last comments
May 28, 2020, 3:14 p.m.
Evgenij Legotskoj

Qt/C++ - Lesson 039. How to paint stroke in QSqlTableModel by value in the column?

Ну в моём примере, который в статье сработало так model->setData(model->index(1, 1), 7); Поскольку model->index(1, 0) - это индекс колонки id, которая скрыта, поэтому…
MA
May 28, 2020, 3:08 p.m.
Mihail A

Qt/C++ - Lesson 039. How to paint stroke in QSqlTableModel by value in the column?

Спасибо, завтра првоерю. А model->setData(model->index(1, 0), 7); Тоже заработало?
May 28, 2020, 3:06 p.m.
Evgenij Legotskoj

Qt/C++ - Lesson 039. How to paint stroke in QSqlTableModel by value in the column?

Да, метод data всё-таки влиял, я переписал его так и заработало удаление QVariant TableModel::data(const QModelIndex &idx, int role) const{ if (role == Qt::BackgroundColorRole) {…
May 28, 2020, 2:49 p.m.
Evgenij Legotskoj

Django - Tutorial 011. Adding comments to the site based on Django

Он более функциональный и его функционал объективно лучше поддерживается Django. Из первого, что приходит на ум: Это наличие полей типа Array Поддержка полей для JSON …
May 28, 2020, 2:42 p.m.
progammist

Django - Tutorial 011. Adding comments to the site based on Django

а в чем явное преимущество postgresql над mysql?)
Now discuss on the forum
IP
May 29, 2020, 1:55 a.m.
Igor' Poroshin

QTablwView + QSqlQueryModel скрыть пустой столбец

Да, понятно. В данном случае лучше использовать серверную процедуру (если такие поддерживаются), в которой будет проверяться наличие всех пустых строк у нужного столбца и вызываться соответ…
RG
May 28, 2020, 6:21 p.m.
Rovshan Gurbanov

Сборка под старые версии Android

У меня SDK почти все версии есть, NDK есть версии 10, 17, 21. Но собирается приложение только с NDK v21 под Android версии 7.0 и выше Версия Qt у меня 5.14.2
May 28, 2020, 7:58 a.m.
Evgenij Legotskoj

Освобождение памяти QMainWindow::setCentralWidget

Да, соглашусь. Просто удаление происходит позже, а не сразу.
May 28, 2020, 5:43 a.m.
Mihailll

При подключении к git как указать пароль?

Нужно сделать ssh-keygen и потом полученый из файла код скопировать в ssh ключ в бикбакете
F
May 28, 2020, 1:42 a.m.
Fidan

QML

Да, проблема ушла, спасибо.
About
Services
© EVILEG 2015-2020
Recommend hosting TIMEWEB