Privacy policyContactsAbout siteOpinionsGitHubDonate
© EVILEG 2015-2018
Recommend hosting
TIMEWEB

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

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.
R
  • #
  • Jan. 5, 2017, 7:20 a.m.

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

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

R

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

ИМ
Здравствуйте Евгений! Чему может быть причиной эта ошибка:
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')
]
ИМ

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

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

<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>
ИМ

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

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

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

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


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]
ИМ

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

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

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

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

Comments

Only authorized users can post comments.
Please, Log in or Sign up
ГК
March 20, 2019, 9:01 a.m.
Геннадий Костоянский

C++ - Test 002. Constants

  • Result:0points,
  • Rating points-10
ГК
March 20, 2019, 8:46 a.m.
Геннадий Костоянский

C++ - Test 002. Constants

  • Result:25points,
  • Rating points-10
Last comments
MU
March 20, 2019, 3:43 p.m.
Maciej Urmański

It's possible to simply add vote option for non logged users?
March 20, 2019, 9:45 a.m.
Евгений Легоцкой

Добрый день. Поппробуйте домен localhost, а url соответственно http://localhost Возможно, потребуется указать порт. Например, так http://localhost:8000
March 19, 2019, 12:57 p.m.
AlexanderBardin

Добрый день. А проверить работоспособность локально как-то можно не указывая реальнй сайт (еще в разработке)
March 16, 2019, 1:55 p.m.
Дмитрий

Спасибо за статью. Давно итересует следующий вопрос: с помощью переменных QMAKE_TARGET_COMPANYQMAKE_TARGET_PRODUCTQMAKE_TARGET_DESCRIPTIONможно задать свойства компилируемой программы, о...
JS
March 12, 2019, 10:19 a.m.
Jean Stefanovich

Большое спасибо за разъяснения!
Now discuss on the forum
March 20, 2019, 12:26 p.m.
Евгений Легоцкой

Лучше стараться избегать этого. Нормального механизма нет. Я просто выдёргиваю из бэкенда перевёденные куски шаблона, если нужно что-то задействовать в JS.
March 17, 2019, 10:47 p.m.
Евгений Легоцкой

Добрый день. Вот, нашлось у меня немного времени. Делается это через шаблон проектирования наблюдатель. GraphKS_mfvSlup.zip
ЧГ
March 15, 2019, 9:52 p.m.
Чарльз Грин

спасибо, попробую, отпишусь
m
March 15, 2019, 7:41 p.m.
mihamuz

Сори догадался)
n
March 12, 2019, 4:57 p.m.
newbie.works.with.QT

Большооооое спасибо!!!!!Не передать как я вам благодарен, спасибо что всегда отзываетесь.Теперь я смогу продолжить работу в QT!!! (пробую писать бота (Я как вы могли догадаться немного не пр...
Join us in social networks

For registered users on the site there is a minimum amount of advertising