How to get highest value in django model for each objects - python

Admin wants to add different challenges. Each challenge has a lot of users. each user may have a lot of likes. I want to show the winner of each challenge. For that, I need to get which candidate gets the highest likes. How can I get it? is there any way like .count .?
how can I use that? in which model.
For example:
challenges
1 first_contest
2 second_contest
candidates
id name contest
1 jhon first_contest
2 sara second_contest
3 abi first_contest
candidates likes
id user_id candidate_id
1 1 1
2 2 2
3 1 1
In this case candidate, 1 = Jhon get 2 likes so in the first contest Jhon wins. Also in the second contest, Sara gets 1 like. So I need to show the winner in the first contest. How is that?
models.py:
class Challenge(models.Model):
title = models.CharField(max_length=50)
class Candidates(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
image = models.FileField( upload_to="challenge_candidates/",)
def likes_count(self):
return self.likes.all().count()
class CandidateLikes(models.Model):
like = models.CharField(max_length=10)
user =
models.ForeignKey(User,on_delete=models.CASCADE,related_name='candidate_likes')
contest_candidates = models.ForeignKey(Candidates, on_delete=models.CASCADE,
related_name='likes')
Sorry for my poor English. Thank you.

You first need to have a relationship between your CandidatLike model and Challenge model so that you can filter by challenge. A foreign key relation could be sufficient. Then you can add this query to your views
winner = CandidateLikes.objects.filter(challenge="your_challenge_name").order_by("like")
Notice that challenge should exist in your CandidatLike model, since we are filtering by it.

I think you are missing a relationship between Challenge and Candidates. Let's say you would add a challenge field to Candidate:
class Candidates(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
challenge = models.ForeignKey(Challenge, on_delete=models.CASCADE, related_name='candidates')
Then you can query the winner of each challenge with the highest like-count with a subquery like this:
from django.db.models import Count
from django.db.models import OuterRef, Subquery
cadidates = Candidates.objects.annotate(like_count=Count('likes')).filter(challange=OuterRef('pk')).order_by('-like_count')
queryset = Challenge.objects.annotate(winner=Subquery(cadidates.values('owner__username')[:1]))
This will give you a Challenge result query with an annotated winner like:
{'id': 1, 'title': 'Challange 1', 'winner': 'username'}

Related

Transforming SQL Query to Django Expression

Assuming I have the following Django models and the according SQL-Tables with some data. I have simplified the models so that it is clearer.
UserAnswer:
class UserAnswer(models.Model):
partquestion = models.ForeignKey(PartQuestion)
answer = models.CharField()
id
answer
partquestion_id
1
667
1
PartQuestion:
class PartQuestion(models.Model):
question = models.ForeignKey(Question)
part = models.ForeignKey(Part)
id
question_id
part_id
1
1
1
Solution:
class SingleInputSolution(models.Model):
question = models.ForeignKey(Question)
content = models.CharField()
id
content
question_id
1
667
1
2
85
2
I want to get all User answers where the answer is equal to the solution of the according question. I've came up with this SQL-Query but can't really think of how to implement this into a Djano query:
select * from (useranswer join partquestion on
useranswer.partquestion_id = partquestion.id) join solution on
partquestion.question_id = solution.question_id where answer=content;
This will output the following:
useranswer.id
useranswer.answer
useranswer.partquestion_id
partquestion.id
partquestion.question_id
partquestion.part_id
solution.id
solution.content
solution.question_id
1
667
1
1
1
1
1
667
1
I just can't get my head around this concept in Django. Maybe using F-Expressions and some stuff.
Thanks for any advice.
You can just use values and the __ relations to traverse the foreign keys.
UserAnswer.objects.values('id', 'answer', 'partquestion_id', 'partquestion_set__id', 'partquestion_set__question_id')
Unfortunately it isn't obvious enough to me how to get those columns for the last table, but I hope that from this answer you can see how to do it?
Okay well I just figured it out myself after reading the answer and comments from #Swift.
It's:
UserAnswer.objects.filter(partquestion__question__solution__content=F('answer'))
It's pretty simple now after I got it but I just tried using solution_set instead of just solution.
If you define your models like this:
class UserAnswer(models.Model):
question = models.ForeignKey(questions,on_delete=models.CASCADE)
user_answer = models.CharField()
class questions(models.Model):
question = models.CharField()
answer = models.CharField()
then this filter must work
right_answers = UserAnswer.objects.filter(question__answer = user_answer )

Django query users that like each other

I have a Django model that looks like this:
class Matches(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
voter = models.ForeignKey(User, related_name='given_vote', on_delete=models.CASCADE)
vote = models.BooleanField(default=False)
I am trying to write a query using django's ORM, but am stuck. Given a user (let's say user_1), I want to return all rows where user_1 has voted True on another user (let's say user_2) AND user_2 has voted True on user_1.
I'm thinking I might need to use Django's Q function but not sure. Here is what I've got:
class User:
def calculate_matches(self):
return Matches.objects.filter(Q(voter=self, vote=True) & Q(user=self, vote=True))
Consider User with ID 1 voted to User with ID 2 voted and vice versa.
Then relevant sql is
SELECT "users_matches"."id", "users_matches"."user_id", "users_matches"."voter_id", "users_matches"."vote" FROM "users_matches" WHERE ("users_matches"."vote" = True AND "users_matches"."voter_id" = 1 AND "users_matches"."user_id" = 2 AND "users_matches"."vote" = True)
And Django ORM you tried looks ok.
Matches.objects.filter(Q(voter=1, vote=True) & Q(user=2, vote=True))
Please include more information if you don't get exact output, mention which output you expect with sample output.
I think it should be something like this:
class User:
def calculate_matches(self):
return Matches.objects.filter(Q(voter=self) | Q(user=self), vote=True)
Means, it will return all the matches where voter is user himself/herself or he is the user when some else voted him.

django object get two fields into a list from a model

this is my model
class pdUser(models.Model):
Name = models.CharField(max_length=200)
Mobile = models.CharField(max_length=200)
PagerDutyID = models.CharField(max_length=200)
PagerDutyPolicyID = models.CharField(max_length=200)
PagerDutyPolicy = models.CharField(max_length=200)
Want i want to be able to do is group by PagerDutyPolicy & PagerDutyPolicyID and return that as an object of its own with unique values only
so for example
Name: bob
PagerDutyPolicyID: 232
PagerDutyPolicy: Team 1
Name: Bill
PagerDutyPolicyID: 232
PagerDutyPolicy: Team 1
Name: Fred
PagerDutyPolicyID: 145
PagerDutyPolicy: Team 2
what i need is an object that has only got
PolicyID: 145
Policy: Team 2
PolicyID: 232
Policy: Team 1
in it, how would i do this?
Thanks
You'll need two models, and a foreign key between them, e.g.:
from django.contrib.auth.models import User
class PagerDutyPolicy(models.Model):
# the model automatically gets an id field
policy_name = models.CharField(max_length=200)
class PagerDuty(models.Model):
# I'm assuming you wanted these to be related to users who can log in..
user = models.ForeignKey(User)
mobile = models.CharField(max_length=200)
policy = models.ForeignKey(PagerDutyPolicy)
To get all policies:
PagerDutyPolicy.objects.all()
To create a new PagerDuty object for bob, butting him in Team 1:
PagerDuty.objects.create(
user=User.objects.get(username='bob'), # or create a new user
mobile='...',
# policy=PagerDutyPolicy.objects.get(policy_name='Team 1') # or..
policy=PagerDutyPolicy.objects.get(id=232)
)
if you're going to look up policies by policy_name that field should also have a db_index=True in the model definition.
something like this:
get all (ID, Team) tuples, make unique
id_teams = pdUser.objects.values_list('id', 'team', flat=True)
id_teams = set(id_teams)
turn into objects
import collections
IDTeam = collections.namedtuple('IDTeam', ['id', 'team'])
output = [IDTeam(id=id, team=team)
for id,team in id_teams]
You can combine values and distinct methods like this:
pdUser.objects.all().values("PagerDutyPolicyID", "PagerDutyPolicy").distinct()
However this will produce fields with PagerDutyPolicyID, PagerDutyPolicy names

Find related objects and display relation

I am using django-follow to allow users to "follow" objects - in this example, Actors in films.
I am pulling back a list of film actors using
actors_user_is_following = Follow.objects.get_follows(Actor).filter(user=request.user.id)
But what I also want to do is suggest films to the user based on the actors they are following. This does not need to be a complex algorithm of what they already like and suggesting relative films, just a simple "because you follow this actor and this actor is in this film, suggest it to the user"
I have this rather clunky way of doing this right now...
context['follows'] = {
'actors': Follow.objects.get_follows(Actor).filter(user=request.user.id),
'genres': Follow.objects.get_follows(Genre).filter(user=request.user.id),
}
actor_ids = []
for actor in context['follows']['actors']:
actor_ids.append(actor.target_artist_id)
genre_ids = []
for artist in context['follows']['genres']:
genre_ids.append(artist.genre_ids)
context['suggested'] = {
'films': Listing.objects.filter(Q(actors__in=actor_ids) | Q(genres__in=genre_ids))
}
Which works, but I'm sure there is a better way of doing it?
Most importantly I also want to show the user why that film as been recommended by displaying the actors or genres it features that the user is following, so the end result might be something like...
film = {
title: 'Dodgeball'
image: '/images/films/dodgeball.jpg'
followed_actors: ['Ben Stiller', 'Vince Vaughn'] #could be multiple
followed_genres: ['Comedy'] #could be multiple
}
Note I would want to return multiple films.
Here's how my models are coded up:
Film Model defined like so:
from django.db import models
from app.actors.models import Actor
from app.genres.models import Genre
class Film(models.Model):
title = models.CharField(max_length=255)
strapline = models.CharField(max_length=255)
slug = models.SlugField(max_length=100)
image_url = models.CharField(max_length=255)
pub_date = models.DateTimeField('date published')
actors = models.ManyToManyField(Actor)
genres = models.ManyToManyField(Genre)
def __unicode__(self):
return self.title
And Actor Model:
from django.db import models
from follow import utils
class Actor(models.Model):
title = models.CharField(max_length=255)
strapline = models.CharField(max_length=255)
image = models.CharField(max_length=255)
image_hero = models.CharField(max_length=255)
bio = models.TextField()
def __unicode__(self):
return self.title
#followable
utils.register(Actor)
Behind the scenes, Follow objects are essentially a many-to-many relationship with fields added each time you register a model.
Your question just talks about actors, but your code also includes genres. It's not especially hard to cover both, I'm just not sure which way is the way you want it.
I think you can get your film objects in one queryset:
films = Film.objects.filter(Q(actors__in=Actor.objects.filter(follow_set__user=request.user)) |
Q(genres__in=Genre.objects.filter(follow_set__user=request.user))).distinct()
As noted in the docs for __in lookups, some database back ends will give you better performance if you evaluate the subqueries before using them:
actor_ids = list(Actor.objects.filter(follow_set__user=request.user).values_list('id', flat=True))
genre_ids = list(Genre.objects.filter(follow_set__user=request.user).values_list('id', flat=True))
films = Film.objects.filter(Q(actors__in=actor_ids) | Q(genres__in=genre_ids)).distinct()
If you just want to return the matching films, I think those are the most concise way to express it.
For the part where you're adding the reasons to the films - I don't see a more elegant way to handle that than to iterate through the films queryset and add the information by hand. I would definitely define the querysets for actor_ids and genre_ids before doing so, although whether or not I evaluated them early would still depend on the db back end.
annotated_films = []
for film in films:
film.followed_actors = film.actors.filter(id__in=actor_ids)
film.followed_genres = film.genres.filter(id__in=genre_ids)
annotated_films.append(film)

Django: query with ManyToManyField count?

In Django, how do I construct a COUNT query for a ManyToManyField?
My models are as follows, and I want to get all the people whose name starts with A and who are the lord or overlord of at least one Place, and order the results by name.
class Manor(models.Model):
lord = models.ManyToManyField(Person, null=True, related_name="lord")
overlord = models.ManyToManyField(Person, null=True, related_name="overlord")
class Person(models.Model):
name = models.CharField(max_length=100)
So my query should look something like this... but how do I construct the third line?
people = Person.objects.filter(
Q(name__istartswith='a'),
Q(lord.count > 0) | Q(overlord.count > 0) # pseudocode
).order_by('name'))
Actually it's not the count you're interested in here, but just whether or not there are any members in that relationship.
Q(lord__isnull=False) | Q(overlord__isnull=False)
In this case, better resort to raw SQL.
for p in Person.objects.raw('SELECT * FROM myapp_person WHERE...'):
print p

Categories

Resources