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.
Related
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'}
I have "post" objects and a "post like" object with how many likes a post has received by which user:
class Post(models.Model):
text = models.CharField(max_length=500, default ='')
user = models.ForeignKey(User)
class PostLike(models.Model):
user = models.ForeignKey(User)
post = models.ForeignKey(Post)
I can select how many likes a post has received like this:
Post.objects.all().annotate(likes=Count('postlike'))
This roughly translates to:
SELECT p.*,
Count(l.id) AS likes
FROM post p, postlike l
WHERE p.id = l.post_id
GROUP BY (p.id)
It works. Now, how I can filter the Count aggregation by the current user? I'd like to retrieve not all the likes of the post, but all the likes by the logged user. The resulting SQL should be like:
SELECT p.*,
(SELECT COUNT(*) FROM postlike WHERE postlike.user_id = 1 AND postlike.post_id = p.id) AS likes
FROM post p, postlike l
WHERE p.id = l.post_id
GROUP BY (p.id)
Do you know the Count has a filter argument?
Post.objects.annotate(
likes=Count('postlike', filter=Q(postlike__user=logged_in_user))
)
It's not exactly as clean, but you could use Case/When...
posts = Post.objects.all().annotate(likes=models.Count(
models.Case(
models.When(postlike__user_id=user.id, then=1),
default=0,
output_field=models.IntegerField(),
)
))
And of course, you can always drop down to .extra() or even raw SQL when there's something you can't express via the Django ORM.
Try to add filter first:
Post.objects.filter(postlike__user=request.user).annotate(likes=Count('postlike'))
From the docs:
The filter precedes the annotation, so the filter constrains the objects considered when calculating the annotation.
I have the following model:
...
from django.contrib.auth.models import User
class TaxonomyNode(models.Model):
node_id = models.CharField(max_length=20)
name = models.CharField(max_length=100)
...
class Annotation(models.Model):
...
taxonomy_node = models.ForeignKey(TaxonomyNode, blank=True, null=True)
class Vote(models.Model):
created_by = models.ForeignKey(User, related_name='votes', null=True, on_delete=models.SET_NULL)
vote = models.FloatField()
annotation = models.ForeignKey(Annotation, related_name='votes')
...
In the app, a User can produce Vote for an Annotation instance.
A User can vote only once for an Annotation instance.
I want to get a query set with the TaxonomyNode which a User can still annotate a least one of its Annotation. For now, I do it that way:
def user_can_annotate(node_id, user):
if Annotation.objects.filter(node_id=node_id).exclude(votes__created_by=user).count() == 0:
return False
else:
return True
def get_categories_to_validate(user):
"""
Returns a query set with the TaxonomyNode which have Annotation that can be validated by a user
"""
nodes = TaxonomyNode.objects.all()
nodes_to_keep = [node.node_id for node in nodes if self.user_can_annotate(node.node_id, user)]
return nodes.filter(node_id__in=nodes_to_keep)
categories_to_validate = get_category_to_validate(<user instance>)
I guess there is a way to do it in one query, that would speed up the process quite a lot. In brief, I want to exclude from the TaxonomyNode set, all the nodes that have all their annotations already voted once by the user.
Any idea of how I could do it? With django ORM or in SQL?
I have Django version 1.10.6
Try to use this:
#SQL query
unvoted_annotations = Annotation.objects.exclude(votes__created_by=user).select_related('taxonomy_node')
#remove duplicates
taxonomy_nodes=[]
for annotation in unvoted_annotations:
if annotation.taxonomy_node not in taxonomy_nodes:
taxonomy_nodes.append(annotation.taxonomy_node)
There would be only one SQL query as select_related will return the related taxonomy_node in a single query. Also there might be a better way to remove duplicates, eg: by using .distinct().
What I have done so far:
taxonomy_node_pk = [a[0] for a in Annotation.objects.exclude(votes__created_by=user)
.select_related('taxonomy_node').values_list('taxonomy_node').distinct()]
nodes = TaxonomyNode.objects.filter(pk__in=taxonomy_node_pk)
I am doing two queries but the second one is not very costly.
It is quite faster than my original version.
Still what I do is not really beatifull. There is no way to get a query set of TaxonomyNode from the Annotation set directly? And then applying disctinct() in it?
I would like to represent my data in a form of a chart,
but i am having a problem making the right queries to display the chart's labels and series ,
and basically i have a Book model , and a User model
and i want to display the number of books(series) that belongs to a particular user(labels) .
models.by
class Book(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=200)
template_tag
#register.simple_tag()
def chart_data():
users =
books =
return json.dumps({
'labels':[user.username for user in users],
'series':[[book.name for book in books]]
})
Something like this:
users = User.objects.all()
for user in users:
books = Book.objects.filter(user=user)
If I understand correctly, You need a json data which should have a label represents username and series representing book.name for that particular user.
Have you tried :
books = Book.objects.values('user__username', 'name').order_by('user__username', 'name')
If you want to do it for a particular user, then:
series = Books.objects.filter(user = "username").values("name")
This will produce a queryset:
<queryset: [{name: book1}, {name: book2}, ...]>
You can convert it to list like so:
list(series)
and then send it through json.dumps..
In the above case, you already know the user those books belongs too, but if you want specify it through the query, then:
series = Books.objects.filter(user = "username").values("name", "user__username")
This will produce something like this:
<queryset: [{name: book1, user: username1}, {name: book2, user: username1}, ...]>
I have a model like below:
class StaffProfile(models.Model):
user = models.ForeignKey(User)
maas = models.FloatField()
maas_gunu = models.CharField(max_length=5)
When I try to insert data with a code like below:
staffprofilesay = StaffProfile.objects.filter(user = user_id).count()
if staffprofilesay > 0:
staffprofile = StaffProfile.objects.get(user = user_id)
else:
staffprofile = StaffProfile()
staffprofile.user = user_id
staffprofile.maas = calisan_formu.cleaned_data["maas"]
staffprofile.maas_gunu = calisan_formu.cleaned_data["maas_gunu"]
staffprofile.save()
I get an error like this:
Cannot assign "u'1'": "StaffProfile.user" must be a "User" instance.
What am I supposed to do?
PS: I'm using Django's User model
You need to assign a User object e.g.
from django.contrib.auth.models import User
user = User.objects.get(id=user_id)
staffprofile.user = user
user needs to be an instance of the User model, not a unicode object (which is what you are passing it).
Yes you have to pass User instance in staffprofile.user = user_id user id place.
As #david-s pointed out in a comment, if you don't have a user instance, you have to fetch from DB with an additional query.
Instead you can directly do is
staffprofile.user_id = user_id because Django behind the scene append _id in table for foreign keys so staffprofile.user will end staffprofile.user_id