How to display top 5 user list in Django? - python

I created a system with Django. In this system, users do some analysis. I keep the information of these analyzes in a model named "ApprocalProcess". How can I display the top 5 users who made the most analysis in "ApprovalProcess"?
models.py
class ApprovalProcess(models.Model):
id = models.AutoField(primary_key=True)
user_id = models.ForeignKey(UserProfile, on_delete=models.CASCADE, null=True, related_name='starter')
doc_id = models.ForeignKey(Pdf, on_delete=models.CASCADE, null=True)
...
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, null=True)
views.py
def approval_context_processor(request):
approval_list = ApprovalProcess.objects.filter(user_id__company=current_user.company)
context = {
'approval_list ': approval_list,
....
}
return context

from django.db.models import Count
approval_list = ApprovalProcess.objects.values('user_id').annotate(Count('user_id')).order_by('-user_id__count')[:5]
First we find user_id duplicate in table and count it. Then we order descending with - in -user_id__count and get 5 users duplicatest in table.
Sorry for my bad English.

Related

What's the most efficient way to retrieve django queryset with the hightest number of posts for a related name?

I'm currently working on a website where advertisements will be posted to display vehicles for sale and rent. I would like to retrieve a queryset that highlights only one car brand (i.e. Audi) which has the highest number of posts for the respective model. Example:
Displaying the Audi brand because it has the highest number of related posts.
My question is, what's the most efficient way of doing this? I've done some work here but I'm pretty sure this is not the most efficient way. What I have is the following:
# Algorithm that is currently retrieving the name of the brand and the number of related posts it has.
def top_brand_ads():
queryset = Advertisement.objects.filter(status__iexact="Published", owner__payment_made="True").order_by('-publish', 'name')
result = {}
for ad in queryset:
# Try to update an existing key-value pair
try:
count = result[ad.brand.name.title()]
result[ad.brand.name.title()] = count + 1
except KeyError:
# If the key doesn't exist then create it
result[ad.brand.name.title()] = 1
# Getting the brand with the highest number of posts from the result dictionary
top_brand = max(result, key=lambda x: result[x]) # Returns for i.e. (Mercedes Benz)
context = {
top_brand: result[top_brand] # Retrieving the value for the top_brand from the result dict.
}
print(context) # {'Mercedes Benz': 7} -> Mercedes Benz has seven (7) related posts.
return context
Is there a way I could return a queryset instead without doing what I did here or could this be way more efficient?
If the related models are needed, please see below:
models.py
# Brand
class Brand(models.Model):
name = models.CharField(max_length=255, unique=True)
image = models.ImageField(upload_to='brand_logos/', null=True, blank=True)
slug = models.SlugField(max_length=250, unique=True)
...
# Methods
# Owner
class Owner(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
telephone = models.CharField(max_length=30, blank=True, null=True)
alternate_telephone = models.CharField(max_length=30, blank=True, null=True)
user_type = models.CharField(max_length=50, blank=True, null=True)
payment_made = models.BooleanField(default=False)
expiring = models.DateTimeField(default=timezone.now)
...
# Methods
# Advertisement (Post)
class Advertisement(models.Model):
STATUS_CHOICES = (
('Draft', 'Draft'),
('Published', 'Published'),
)
owner = models.ForeignKey(Owner, on_delete=models.CASCADE, blank=True, null=True)
name = models.CharField(max_length=150, blank=True, null=True)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE, blank=True, null=True)
publish = models.DateTimeField(default=timezone.now)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='Draft')
...
# Other fields & methods
Any help would be greatly appreciated.
Since you need brands, let's query on Brand model:
Brand.objects.filter(advertisement__status__iexact="Published").\
filter(advertisement__owner__payment_made=True).\
annotate(published_ads=Count('advertisement__id')).\
order_by('-published_ads')
However, even in your proposed solution, you can improve a little bit:
Remove the order_by method from your queryset. It doesn't affect the final result but adds some overhead, especially if your Advertisement model is not indexed on those fields.
Every time you call ad.brand you are hitting the database. This is called the N+1 problem. You are in a loop of n, you make n extra db access. You can use select_related to avoid such problems. In your case: Advertisement.objects.select_related('brand')...
Did you try the count method?
from django.db.models import Count
Car.objects.annotate(num_views=Count('car_posts_related_name')).order_by('num_views')

How to get the most liked users on a particular date in django

So I have a social media app, where users can like the posts of other users. Now I fetch the top 20 users who have received the most number of likes. Everything is perfect. But the problem is I cant figure out , how I can fetch the top users who have received the most likes on a particular date, for example get the top users who received most likes only today
My LIKES MODEL
class PostLike(models.Model):
user_who_liked = models.ForeignKey(User, on_delete=models.CASCADE)
post_liked = models.ForeignKey(Post, on_delete=models.CASCADE)
liked_on = models.DateTimeField(default=timezone.now)
SIMPLIFIED POST MODEL
class Post(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE,related_name='author')
caption = models.TextField()
date = models.DateTimeField(default=timezone.now)
likes = models.ManyToManyField(
User, blank=True, through=PostLike)
image = models.TextField()
class Meta:
ordering = ['-id']
SIMPLIFIED USER MODEL
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
user_name = models.CharField(max_length=100, unique=True)
date = models.DateTimeField(default=timezone.now)
profile_picture = models.TextField(
default="https://www.kindpng.com/picc/m/24-248253_user-profile-default-image-png-clipart-png-download.png")
bio = models.CharField(max_length=200, default="")
objects = CustomManger()
def __str__(self):
return self.user_name
** My query to get the top users who received the most number of likes **
users = User.objects.annotate(num__liked=Count('author__likes')).order_by('-num__likes')[:20]
# So everything is perfect and I am getting the users, now I dont know how to get the top users with most likes on a PARTICULAR DATE, for example today
** My try to get the top users with most likes on a particular day**
from django.db.models import Count, Q
from django.utils.timezone import datetime
users = User.objects.annotate(num__liked=Count('author__likes',filter=Q(author__likes__liked_on = datetime.today()))).order_by('-num__likes')[:20]
But with the above query , I cant achieve it. I am getting the error:
Related Field got invalid lookup: liked_on
I am pretty sure, I am doing something wrong with the many-many fields.
Q(author__likes__liked_on = datetime.today()) won't work, because liked_on is a datetime, while datetime.today() is a date. And the filtered field is on the 'through' table.
So you need to cast liked_on to a date, and look up the field on postlike (lower-cased by default):
Q(author__postlike__liked_on__date = datetime.today()))

List of Users ordered by the rank of their Posts reviews

I want to make an API End Point so the user can get a list of the users in his city ordered by their post reviews
I have defined a method in the post model to calculate the total review (up vote and down vote), I'm imagining that the solution can be realized in the following path but I'm not entirely sure groupBy post_owner in the post and orderBy sum(count_reactions()), but I don't know how to do it in django
Post Model
class Post(models.Model):
title = models.TextField(max_length=255, default='Title')
post_owner = models.ForeignKey(MyUser, on_delete=models.CASCADE)
description = models.TextField(max_length=255)
city = models.ForeignKey(City, related_name='location', on_delete=models.CASCADE)
longitude = models.CharField(max_length=255)
image = models.CharField(max_length=255,
default='https://www.eltis.org/sites/default/files/styles/web_quality/public/default_images/photo_default_2.png')
latitude = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
def count_reactions(self):
likes_count = Reaction.objects.filter(post=self.id, is_like=True).count()
dislikes_count = Reaction.objects.filter(post=self.id, is_like=False).count()
return likes_count - dislikes_count
def owner(self):
return self.post_owner
MyUser Model
class MyUser(AbstractUser):
phone_number = models.BigIntegerField(blank=False, unique=True)
city = models.ForeignKey(City, related_name='city', on_delete=models.CASCADE)
address = models.CharField(max_length=255)
def owner(self):
return self
Reaction Model
class Reaction(models.Model):
reaction_owner = models.ForeignKey(MyUser, on_delete=models.CASCADE)
post = models.ForeignKey(Post, related_name='reactions', on_delete=models.CASCADE)
is_like = models.BooleanField(null=False)
def owner(self):
return self.reaction_owner
The expected result is to get the ordered list of the users by their posts reviews but only the users in the same city (city field in MyUser model)
You can put it all into one query.
Depending on where your Reaction naming the query should look something like this:
# Filter for the city you want
users = MyUser.objects.filter(city=your_city_obj)
# Then doing the calculations
users = users.annotate(rank_point=(Count('post__reactions', filter=Q(post__reactions__is_like=True)) - (Count('post__reactions', filter=Q(post__reactions__is_like=False)))))
# And finaly, order the results
users = users.order_by('-rank_point')
The answer is Navid's answer but completing it with excluding the users with rank equal to zero and include also the limit
# Filter for the city you want
users = MyUser.objects.filter(city=your_city_obj)
# Then doing the calculations
users = users.annotate(rank_point=(Count('post__reactions', filter=Q(post__reactions__is_like=True)) - (Count('post__reactions', filter=Q(post__reactions__is_like=False))))).filter(rank_point__gt=0)
# And finaly, order the results
users = users.order_by('-rank_point')[:LIMIT]

Django rating model with m2m

I'm confused with my django models,
My models:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
picture = models.ImageField(upload_to="photos/", default="photos/none/default.png")
film = models.ManyToManyField(Film)
class Film(models.Model):
title = models.CharField(max_length=60)
year = models.IntegerField(choices=YEARS)
image = models.ImageField(upload_to="images/", default="images/none/blank_poster.jpg")
def __str__(self):
return self.title
and now I trying to make a ratings for my film, when user adding a film to his list.
I tried M2M with through, but it wasn't exactly what I wanted, because user could add the same film several times and another problem with it was remove single film from list.
Now i thinking about additional models like this:
class FilmRating(models.Model):
user = models.ForeignKey(User)
film = models.ForeignKey(Film)
rate = models.IntegerField(choices=CHOICES)
Im glad If you can point me to the correct way to solve this problem, In future I want probably to store all rating from users for set average rate.

Django COUNT with JOIN and GROUP BY SQL query

What would be the correct Django view and HTML for this SQL query?:
SELECT
hood.`hood`,
COUNT(business.`id`) AS TOTAL
FROM
`hood`
JOIN business
ON hood.`id` = business.`hood_id`
WHERE business.`city_id` = 8
GROUP BY hood.`id`
ORDER BY TOTAL DESC
LIMIT 5 ;
My models are:
class Hood(models.Model):
name = models.CharField(max_length=50, db_column='hood')
slugname = models.SlugField(max_length=50, blank=True)
city = models.ForeignKey('City', related_name='hoods')
location = models.ForeignKey('Location', related_name='hoods')
switch = models.SmallIntegerField(null=True, blank=True, default='1')
class Meta:
db_table = 'hood'
class Business(models.Model):
name = models.CharField(max_length=50, db_column='name', blank=True)
slugname = models.SlugField(max_length=50, blank=True)
city = models.ForeignKey('City', related_name="business")
hood = models.ForeignKey('Hood', null=True, blank=True, related_name="business")
....
And the HTML template?
Thank you!
Check out the docs on aggregration:
https://docs.djangoproject.com/en/1.6/topics/db/aggregation/
You should be able to write a view that returns a queryset with counts similar to this:
from django.db.models import Count
Hood.objects.filter(business__city_id=8).annotate(bus_count=Count('business__id'))
As for the HTML, that's entirely up to you. If you provide that queryset, though, you'd be able to get the count with {{ object.bus_count }}.

Categories

Resources