Search code examples
pythondjangostatisticsdjango-querysetslug

Python - retrieving slug value from foreignkey class


I'm a newbie in Python and I need some help with my code. Not even sure if my title makes sense.

Basically I have my blog and I'm trying to add a sidebar with popular posts. I have created a PostStatistics class to collect the number of visits in each post which can be seen from Django admin.

The PostStatistics class has a ForeignKey to the Post class.

OK, so my problem is in the PostDetail view. I have a QuerySet there called Popular where I retrieve the 5 most popular posts in the last 7 days. There I retrieve the Post_id and Post__Title. I also need to retrieve the Post SLUG but I have no idea how I can do that.

The slug would be used in the following bit of code:

<a href="{% url 'post_detail' pop_article.post_slug %}">{{ pop_article.post__title }}</a>

The following is what in my models:

class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(
    User, on_delete=models.CASCADE, related_name='blog_posts')
updated_on = models.DateTimeField(auto_now=True)
content = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
views = models.PositiveIntegerField(default=0, editable=False)

class Meta:
    ordering = ['-created_on']
    db_table = "post"

def __str__(self):
    return self.title

def get_absolute_url(self):
    from django.urls import reverse
    return reverse("post_detail", kwargs={"slug": str(self.slug)})

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

post = models.ForeignKey(Post, on_delete=models.CASCADE)                    
date = models.DateField('Date', default=timezone.now)   
views = models.IntegerField('Views', default=0)

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

The following is what is in my views:

def PostDetail(request, slug):
template_name = 'post_detail.html'
post = get_object_or_404(Post, slug=slug)
comments = post.comments.filter(active=True)
new_comment = None
context = {}

obj, created = PostStatistic.objects.get_or_create(
    defaults={
        "post": post,
        "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(), post=post    
)

obj.views += 1    
obj.save(update_fields=['views'])
        # Now pick up the list of the last 5 most popular articles of the week
popular = PostStatistic.objects.filter(
    # filter records in the last 7 days
    date__range=[timezone.now() - timezone.timedelta(7), timezone.now()]
).values(
    'post_id', 'post__title'
).annotate(
    views=Sum('views')
).order_by(
    # sort the records Descending
    '-views')[:5]  # Take 5 last records


# Comment posted
if request.method == 'POST':
    comment_form = CommentForm(data=request.POST)
    if comment_form.is_valid():

        # Create Comment object but don't save to database yet
        new_comment = comment_form.save(commit=False)
        # Assign the current post to the comment
        new_comment.post = post
        # Save the comment to the database
        new_comment.save()
else:
    comment_form = CommentForm()

return render(request, template_name, {'post': post,
                                       'comments': comments,
                                       'new_comment': new_comment,
                                       'comment_form': comment_form,
                                       'popular_list': popular
                                       },)

The following is in my HTML:

        <div class="card-body">
        {% if popular_list %}
        <p class="card-text">
            {% for pop_article in popular_list %}
            <a href="{% url 'post_detail' pop_article.post_slug %}">{{ pop_article.post__title }}</a>
            <br>
            {% endfor %}
        </p>
        {% endif %}
    </div>

Thanks in advance!


Solution

  • you need to add post__slug in values of this query in view function like this

    popular = PostStatistic.objects.filter(
        # filter records in the last 7 days
        date__range=[timezone.now() - timezone.timedelta(7), timezone.now()]
    ).values(
        'post_id','post__slug' ,'post__title'
    ).annotate(
        views=Sum('views')
    ).order_by(
        # sort the records Descending
        '-views')[:5]
    

    then you will be able to do like this in the template

    <a href="{% url 'post_detail' pop_article.post__slug %}">{{ pop_article.post__title }}</a>