I am only learning django so any guide would be much appreciated. I am creating an app where authenticated users can like other posts. On one of the pages, I would like to render and show the post with most likes. Please see my code below:
models.py:
class Enter(models.Model):
title = models.CharField(
max_length=200, unique=True, null=False, blank=False)
slug = models.SlugField(
max_length=200, unique=True, null=False, blank=False)
competitor = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='post_enter')
updated_on = models.DateTimeField(auto_now=True)
content = models.TextField()
featured_image = CloudinaryField('image', null=False, blank=False)
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
likes = models.ManyToManyField(User, related_name='post_likes', blank=True)
class Meta:
ordering = ['-created_on']
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Enter, self).save(*args, **kwargs)
def __str__(self):
return self.title
def number_of_likes(self):
return self.likes.count()
views.py:
class CurrentOrderList(generic.ListView):
model = Enter
queryset = Enter.objects.filter(status=1).order_by('likes')[0]
template_name = 'competition.html'
competition.html:
<div class="container-fluid">
{% for entry in enter_list %}
<div class="row justify-content-center mt-4">
<div class="col-md-4">
<div class="card mb-4">
<div class="card-body">
<div>
<img class="card-img-top" src=" {{ entry.featured_image.url }}">
<div>
<p class="competitor text-muted">By: {{ entry.competitor }}</p>
</div>
</div>
<h2 class="card-title">{{ entry.title }}</h2>
<hr />
<p class="card-text text-muted h6">{{ entry.created_on}} <i class="far fa-heart"></i>
{{ entry.number_of_likes }}</p>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
If I remove [0]
(or .first()
) from the views.py list does get rendered on the page, but some of the items get duplicated - ** using .distinct()
does not remove duplicates**
How can I display the entry with highest amount of likes? Thank you!
You probably want to order by the number of likes, that is something else than order by likes. By doing .order_by('-likes')
, you order in descending order by the primary key of the liker, and thus the same Enter
appears as much as there are likers.
If you want to order by the number of likes, you work with:
from django.db.models import Count
class CurrentOrderList(generic.ListView):
model = Enter
queryset = (
Enter.objects.filter(status=1)
.alias(nlikes=Count('likes'))
.order_by('-nlikes')
)
template_name = 'competition.html'
Using [0]
or .first()
however makes no sense, then these are not querysets, but Enter
objects. The queryset
should have a value of type QuerySet
, not a model object.
If you only want to show the most liked post, this is a DetailView
, and you thus use get_object
instead:
from django.db.models import Count
class CurrentOrderList(generic.DetailView):
model = Enter
queryset = (
Enter.objects.filter(status=1)
.alias(nlikes=Count('likes'))
.order_by('-nlikes')
)
template_name = 'competition.html'
def get_object(self, *args, **kwargs):
return self.get_queryset().first()
then it passes that object as object
to the template, so you don't use a {% for … %}
to enumerate, you only need to render object
.