Getting duplicate users in leaderboard - python

I'm trying to make a leaderboard for the most liked users by counting the likes of their post and ordering them.
However if the user has 2 or more post, it will duplicate the user in the leaderboard with the total likes that he has. Is there a way to fix this? Thank you.
models.py:
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
content = models.TextField(max_length=1500, verbose_name='Content')
likes = models.ManyToManyField(User)
views.py:
def top_user(request):
top_user = Post.objects.annotate(total_likes=Count('user__post__likes')).order_by('-total_likes')
context = {'users': top_user}
return render(request, 'blog/top_user.html', context)
html:
{% for top in users %}
<h5>{{ top.user }}</h5>
<p>Upvotes: {{ top.total_likes }}</p>
{% endfor %}

You query the wrong way, the top_users you here present are Posts, so that means that per posts, you count the number of total likes of that author. But if an author has many posts, they appear multiple times.
You thus should annotate the users:
top_user = User.objects.annotate(total_likes=Count('post__likes')).order_by('-total_likes')
and thus render it with:
{% for user in users %}
<h5>{{ user }}</h5>
<p>Upvotes: {{ user.total_likes }}</p>
{% endfor %}

Related

Better way to get associated model in Django from the parent model in view

I have the following model:
models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
city = models.CharField(max_length=250, default='New York', blank=False, null=False)
#other fields
And in views.py I should fetch users and userprofiles so that I can get it in home.html:
views.py
from django.contrib.auth.models import User
def home(request):
users = User.objects.filter(is_staff=True)
context = { 'users': users }
return render(request, 'users/home.html', context)
home.html
{% for user in users %}
<p>{{ user.username }}</p>
<p>{{ user.userprofile.city }}</p>
//other userprofile data
{% endfor %}
Here I am getting the values of userprofile in template. But what if I have about 100000 users? Will this be an efficient query? I need to fetch all 100000 users and display them at a time in a single page. For this particular page pagination is not required. Styling makes them appear very small. So the query should get all 100000 users and their profile values. What is the best way to get those values in template?
Edit
Now I have used
User.objects.filter(is_staff=True).prefetch_related('userprofile')
as stated by #Alexander Lekontsev.
By doubt now is how to render this in template? Should I use the above method specified in the question? Or should I use two for loops?
{% for user in users %}
<p>{{ user.username }}</p>
{% for profile in user.userprofile.all %}
<p>{{ profile.city }}</p>
//other userprofile data
{% endfor %}
{% endfor %}
This method does not work. What is the correct way?

Django get user like from friend list

I'm a Django beginner, I have a friend field in Profile Model which list all my friend users. And also these friends liked my post. How do I get the names of only those users in my friends list to display in the post they liked.
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,... )
friends = models.ManyToManyField('Profile',related_name='my_friends')
class FriendRequest(models.Model):
to_user = models.ForeignKey(settings.AUTH_USER_MODEL,... )
from_user = models.ForeignKey(settings.AUTH_USER_MODEL,... )
class Post(models.Model):
poster_profile = models.ForeignKey(settings.AUTH_USER_MODEL,... )
likes = models.ManyToManyField(User, related_name='image_likes')
def home(request):
all_images = Post.objects.filter(poster_profile=request.user)
img = Post.objects.filter(likes__profile__friends__user=request.user)
{% if all_images %}
{% for post in all_images %}
#all_images code here
{{ post. likes.count }} #like count
{% for image in img %}<img src="{{ image.profile.profile_pic.url }}"> {% endfor %}#The profile_pic do not display
{% endfor %}
{% endif %}
if someone isn't your friend you can prevent him to like or even see (or comment) your post , or for each user who likes your post check if he is in your friend list then add him to a new emty list then render this list
Change your queryset in your view to this:
from django.db.models import Count, Q
all_images = Post.objects\
.filter(poster_profile=request.user)\
.annotate(likes_count=Count(
'likes',
distinct=True,
filter=Q(likes__profile__friends=request.user.profile))
then in your template each post has the annotation likes_count so you can do {{ post.likes_count }}.

Listing ForeignKey associated instances within template (queryset within a queryset)

I have a site which catalogs local hikes, and users can log that they have been on the hike. I have a search page which contains the hikes, and one of the fields I'm trying to display is a list of all the people who have been on the hike. I've got this figured out within the individual detail page of the hike, but can't figure out how to create a new queryset within the queryset which is printing the hikes, in order to display this info on a search page.
Here's some code:
models.py:
class Hike(models.Model):
name = models.CharField(max_length=255, unique=True)
slug = models.SlugField(unique=True)
...
class UserLog(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
hike = models.ForeignKey(Hike, on_delete=models.CASCADE)
views.py:
def hike_list(request):
qs = Hike.objects.all()
... some other filters here
?-->users = UserLog.objects.filter(id=qs.id)
template:
{% for qs in qs %}
{{ hike.name }}{{ hike.other_details_and_stuff }}
?----> {% for users in hikes %}{{ user.name }}{% endfor %}
{% endfor %}
Here's the working code within the individual hike's detail page:
views.py:
def hike_detail (request, slug)
users = UserLog.objects.filter(hike__slug=slug)
How do I call on the slug from each individual item in the queryset, then run a queryset on that?
The easiest is to add a ManyToManyField to Hike:
class Hike(models.Model):
...
users = models.ManyToManyField(User, through='app.UserLog')
If you have no extra fields in UserLog, you can even remove the UserLog model and the through parameter alltogether. In the template you can do:
{% for hike in qs %}
{{ hike.name }}{{ hike.other_details_and_stuff }}
{% for user in hike.users.all %}{{ user.name }}{% endfor %}
{% endfor %}
In order avoid too many queries, you should prefetch the users in the Hike query in the view:
qs = Hike.objects.all().prefetch_related('users')
Without the ManyToManyField, you could add a property and user the same template, but the prefetch clause could not be used that easily:
class Hike(models.Model):
...
#property
def users(self):
return User.objects.filter(userlog__hike=self)

Django: Querying Multiple Foreign Keys

First off, I'm sure this is a simple question. I'm just getting started with Django and as all beginners do, I thought I'd build a simple blog.
I have a simple data model, a Post model that contains with a link to a user via a FK.
models.py
class Post(TimeStampedActivate):
"""
A blog post
"""
title = models.CharField(max_length=255)
slug = models.SlugField()
excerpt = models.TextField(blank=True)
body = models.TextField()
publish_at = models.DateTimeField(default=datetime.datetime.now())
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=False, help_text='Is the blog post active?')
user = models.ForeignKey(User, related_name='post_user')
def __unicode__(self):
return self.title
I then want a page that lists all of the Posts alongside the username of the person who created the post. My current view looks like this:
views.py
def index(request):
posts = Post.objects.filter(active=True)
user = User.objects.get(id=1)
return render(request, 'blog/index.html', {'posts': posts, 'user': user})
My template
At present this just displays the users name that matches an ID of 1.
{% for post in posts %}
<h2>{{ post.title }}</h2>
<p>{{ post.excerpt|truncatewords:30 }}</p>
<p>Posted by {{ user.first_name }} {{ user.last_name }}</p>
{% endfor %}
How would I modify my views.py file to ensure I get the first and last name of the user responsible for the post?
View:
def index(request):
posts = Post.objects.filter(active=True)
return render(request, 'blog/index.html', {'posts': posts})
Template:
{% for post in posts %}
<h2>{{ post.title }}</h2>
<p>{{ post.excerpt|truncatewords:30 }}</p>
<p>Posted by {{ post.user.first_name }} {{ post.user.last_name }}</p>
{% endfor %}
Use "post.user" in your template instead of just "user".
{{ post.user.first_name }} {{ post.user.last_name }}
"user" is the current logged-in user.
If you use RequestContext & added auth to your TEMPLATE_CONTEXT_PROCESSORS, user is equal to request.user. https://docs.djangoproject.com/en/dev/topics/auth/#authentication-data-in-templates

Django count specific items of a many-to-one relationship

I have a Django app where users post messages and other users can vote the answers up or down, very similar to SO. I'm having an issue trying to get the "thumbs up" and "thumbs down" counts from within the template and I'm hoping someone can help me. The PostVote is a many-to-one relationship with the Post class. Here is what my model looks like:
class Post(models.Model):
account = models.ForeignKey(Account)
message = models.CharField(max_length=1024)
timestamp = models.DateTimeField('post timestamp')
class PostVote(models.Model):
post = models.ForeignKey(Post)
account = models.ForeignKey(Account)
vote = models.CharField(max_length=16, choices=VOTE_CHOICES)
timestamp = models.DateTimeField('vote timestamp')
Here is how I'm getting my posts:
posts = Post.objects.all().order_by('-timestamp')[:10]
My template looks roughly like:
{% for post in posts %}
<div>Thumbs up count: {{ WHAT_HERE }}</div>
<div>Thumbs down count: {{ WHAT_HERE }}</div>
{% endfor %}
How can I get the counts in there? I'm sure it involves 'annotate' somehow, but I'm having a difficult time coming up with this one. Any help would be greatly appreciated!
You shouldn't really be doing logic in your templates. Add a couple count methods to your Post model:
class Post(models.Model):
account = models.ForeignKey(Account)
message = models.CharField(max_length=1024)
timestamp = models.DateTimeField('post timestamp')
def upvote_count(self):
return self.postvote_set.filter(vote=VOTE_CHOICES[0][0]).count()
def downvote_count(self):
return self.postvote_set.filter(vote=VOTE_CHOICES[1][0]).count()
Then use them in your template:
{% for post in posts %}
<div>Thumbs up count: {{ post.upvote_count }}</div>
<div>Thumbs down count: {{ post.downvote_count }}</div>
{% endfor %}

Categories