How to change ON expression in django-orm select_related() - python

So i have this models
class ApplicationForHelp(BaseModel):
user = models.ForeignKey(User, related_name="applications", on_delete=models.CASCADE)
tags = models.ManyToManyField(TagsForApplication, related_name="applications")
title = models.CharField(max_length=50)
description = models.TextField()
is_anonymous = models.BooleanField(default=False)
place = models.TextField(null=True)
And user model
so if do ApplicationForHelp.objects.filter().select_related('user')
what it does :
left join ON application.user_id = user.id
what i want :
left join ON (application.user_id = user.id and application.is_anonymous=False)

The documentation for the FilteredRelation objects says:
FilteredRelation is used with annotate() to create an ON clause when a
JOIN is performed.
Given usernames, an iterable of user names, and assuming one is looking for the ApplicationForHelp objects whose user foreign key has a username in usernames and whose is_anonymous field is False:
from django.db.models import FilteredRelation, Q
apps = (
ApplicationForHelp.objects
.annotate(
not_anonymous=FilteredRelation(
'user',
condition=Q(is_anonymous=False)
)
)
.filter(not_anonymous__username__in=usernames)
.select_related('user')
)

Related

Django: how to use .filter( ) method in django?

I am trying to display quiz only for users that are registered in a particular course, i.e if a user is registered in a Frontend Crash Course i want them to see only the quiz related to that course they are registered in, and not all the quiz from the db.
i have a model UserCourse where i am storing all the courses a user have enrolled in, when i try filtering by that models while user_course is get like this below
user_course = UserCourse.objects.get(user=request.user)
quizzes = Quiz.objects.filter(course__usercourse=user_course).annotate(questions_count=Count('questions'))
i get this error get() returned more than one UserCourse -- it returned 3! Now i have changed .get() to .filter() like this
user_course = UserCourse.objects.filter(user=request.user)
quizzes = Quiz.objects.filter(course__usercourse=user_course).annotate(questions_count=Count('questions'))
i then get this error The QuerySet value for an exact lookup must be limited to one result using slicing.
What is the right way to write this query.
models.py
class UserCourse(models.Model):
user = models.ForeignKey(User , null = False , on_delete=models.CASCADE)
course = models.ForeignKey(Course , null = False , on_delete=models.CASCADE, related_name="usercourse")
class Quiz(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="quizzes")
title = models.CharField(max_length=255)
course = models.ForeignKey(Course, on_delete=models.SET_NULL, null=True, related_name="quizzes")
date = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(unique=True)
user_course = models.ForeignKey(UserCourse, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.title
The Problem in the Second Line
user_course = UserCourse.objects.filter(user=request.user)
quizzes=Quiz.objects.filter(course__usercourse=user_course).annotate(questions_count=Count('questions'))
remember that when You are using filter you get QuerySet not one object
if you want to return the quizes those related to user_course_queryset you can use __in filter
print(user_course) # print it to understand more
quizzes=Quiz.objects.filter(course__usercourse__in=user_course)
this will Return every Quiz Related to the QuerySet objects

How can I fetch data by joining two tables in django?

Looking for help got stuck at a point, I am new to python and django. There ARE two payments corresponding to one order, one COLLECTION and multiple TRANSFER and i need the payment corresponding to an order whose direction is COLLECTION only NOT transfered yet so that i can initiate TRANSFER against that order
models.py
class Orders(models.Model):
id= models.AutoField(primary_key=True)
payment_gateway_code = models.CharField(max_length=20,choices=[('PAYTM','PAYTM')])
is_active = models.BooleanField(default=True)
class Payments(models.Model):
id = models.AutoField(primary_key=True)
orders = models.ForeignKey(Orders, on_delete=models.CASCADE)
direction = models.CharField(max_length=20,choices=[('COLLECTION','COLLECTION'),
('TRANSFER','TRANSFER')])
settlement_status = models.CharField(max_length=50,blank=True, null=True,choices=[('YES','YES'),
('NO','NO')])
is_active = models.BooleanField(default=True)
qualified_orders = Orders.objects.filter(payment_gateway_code='CASHFREE',
Exists(Payments.objects.filter(order=OuterRef('pk'), direction='COLLECTION',
settlement_status='YES')), ~Exists(Payments.objects.filter(order=OuterRef('pk'),
direction='TRANSFER')))
But above query is not working
What is OuterRef('pk')?
First, I'd suggest changing orders to order.
Then, the query you're trying to achieve will be something like this (Assuming order_id contains the ID of the order):
Paymen.objects.filter(order_id=order_id, direction="COLLECTION")
You can use views.py for that as follows
Models.py
class Orders(models.Model):
id= models.AutoField(primary_key=True)
payment_gateway_code = models.CharField(max_length=20,choices=[('PAYTM','PAYTM')])
is_active = models.BooleanField(default=True)
class Payments(models.Model):
id = models.AutoField(primary_key=True)
orders = models.ForeignKey(Orders, on_delete=models.CASCADE)
direction = models.CharField(max_length=20,related_name="direction",choices=[('COLLECTION','COLLECTION'),
('TRANSFER','TRANSFER')])
settlement_status = models.CharField(max_length=50,blank=True, null=True,choices=[('YES','YES'),
('NO','NO')])
is_active = models.BooleanField(default=True)
views.py
from App.models import orders, payments
#in case if you need objects of order this is for that
def orderfunc():
order = Orders.objects.all()
def paymentfunc():
payment = Payment.objects.all()
# from here you can check for what record you want using conditional operator
#if direction == COLLECTION:
#then do what you need

Checking if relationship exists with query

I am trying to check whether or not a following relationship exists using a query. First, I get all of the followers the user has and then I check whether or not the user follows those followers. Here are my models:
class Following(models.Model):
target = models.ForeignKey('User', related_name='followers', on_delete=models.CASCADE, null=True)
follower = models.ForeignKey('User', related_name='targets', on_delete=models.CASCADE, null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{} is followed by {}'.format(self.target, self.follower)
class User(AbstractBaseUser):
username = models.CharField(max_length=15, unique=True)
email = models.EmailField(max_length=100, unique=True)
I am using the Django Rest-Framework so I go to the specific URL to get the information I need. After going to the URL, the output is expected. I get all the followers the user has.
views.py
class GetFollowersView(ListAPIView):
serializer_class = FollowingSerializer
def get_queryset(self):
requested_user = get_requested_user(self)
return User.objects.filter(targets__target=requested_user).order_by('-targets__created_at'). \
annotate(is_following=Count('followers__follower', filter=Q(followers__follower=requested_user), distinct=True))
def get_requested_user(self):
filter_kwargs = {'username': self.kwargs['username']}
return get_object_or_404(User.objects.all(), **filter_kwargs)
serializers.py
class FollowingSerializer(serializers.ModelSerializer):
is_following = serializers.IntegerField()
class Meta:
model = User
fields = ('id', 'username', 'follower_count', 'following_count', 'is_following')
However, the problem is in the is_following annotation. I'd like to see whether or not the user follows each specific follower. If they follow that follower, then is_following should be 1 if not, then it is a 0. I'm getting incorrect results in is_following is there a way I can check if the user follows each specific follower?
If you have Django Debug Toolbar installed and you check the query for your current filter/annotate, this is what it shows (for a single user)
SELECT "user"."id", "user"."username", "user"."email",
COUNT(DISTINCT T4."follower_id") AS "is_following" FROM "user"
INNER JOIN "following" ON ( "user"."id" = "following"."follower_id" )
LEFT OUTER JOIN "following" T4 ON ( "user"."id" = T4."target_id" )
WHERE "following"."target_id" = 4 GROUP BY "user"."id", "user"."username",
"user"."email", "following"."created_at" ORDER BY "following"."created_at"
DESC
However to get the count of the users the chosen user follows, you really want something like this
SELECT ue."id", ue."username", ue."email", COUNT(DISTINCT fe."target_id") AS
"is_following" FROM "user" u inner JOIN "following" fe ON ( u."id" =
fe."follower_id" ) inner join user ue on fe.target_id = ue.id and u.id = 4
GROUP BY ue."id", ue."username", ue."email"
I don't think it is possible to combine both the followers and the followee in the same query like you have done. You could possibly find the intersection and then proceed from there...Something like this..
def get_queryset(self):
username = self.request.query_params.get('username', None)
requested_user = models.User.objects.get(username=username)
following_me = models.User.objects.filter(targets__target=requested_user).order_by('-targets__created_at')
i_follow = models.User.objects.filter(followers__follower=requested_user).order_by('-followers__created_at')
common = following_me & i_follow
### Set is_following for common as 1, all others as 0.
#......
#......
return following_me
Why not use an M2M relationship? Seems like this could be simple:
from django.db import models
class User(models.Model):
name = models.CharField(max_length=200)
followers = models.ManyToManyField('User')
#property
def follower_count(self):
# How many people follow me
return len(self.followers)
#property
def followee_count(self):
# How many people I follow
return len(self.user_set.all())
And you can modify the get_queryset() to only find followers:
User.objects.filter(followers__contains=self.request.user)
Does this help?

Django ORM Annotate boolean field that defines is user member of a team

models.py
from django.db import models
class UserGroup(models.Model):
members = models.ManyToManyField(User, related_name='members', through='UserGroupMember')
class UserGroupMember(models.Model):
user = models.ForeignKey(User)
usergroup = models.ForeignKey(UserGroup)
class Cohort(models.Model):
user_groups = models.ManyToManyField(UserGroup)
class Team(models.Model):
cohort = models.ForeignKey(Cohort)
members = models.ManyToManyField(User, related_name='team_members', through='TeamMembers', blank=True)
class TeamMembers(models.Model):
team = models.ForeignKey(Team)
user = models.ForeignKey(User)
Single user can be part of only one team within a cohort.
I want to annotate the new field (boolean) which tells you is the user assigned to some team in the cohort, something like:
User.objects.filter(
members__cohort=cohort
).annotate(
is_team_member=...
)
I am using Python 2.7.13 and Django 1.9.8. Thanks.
I managed to solve the problem by doing the join to the Cohort model and using conditional expressions:
from django.db.models import Case, When, Value, IntegerField
users = User.objects.filter(
members__cohort=cohort
).annotate(
is_team_member=Case(When(team_members__cohort=cohort, then=Value(1)), default=0, output_field=IntegerField())
)
Now I can easily filter, for example, users who are part of some team:
users.filter(is_team_member=1)

Selecting data from few relational tables based on few conditions in Django

I'm using Django 1.6 with Python 2.7 and I have few related models:
# keys/models.py
class Key(models.Model):
user = models.ForeignKey('auth.User')
is_valid = models.BooleanField()
# entities/models.py
class Entity(models.Model):
user = models.ForeignKey('auth.User')
key = models.ForeignKey('keys.Key')
active = models.BooleanField(default=False)
# profile/models.py
class Profile(models.Model):
user = models.ForeignKey('auth.User')
profile_id = models.PositiveIntegerField(null=True, blank=True)
Is it possible to make a single-line query which would check these conditions:
Key.is_valid must be True
Entity.active must be True
Profile.profile_id must not be null (or None)
The only thing I can pass to that query is request.user.
if you are wanting to get Entity objects:
objects = Entity.objects.filter(active=True,
key__is_valid=True,
user__profile__profile_id__isnull=False)
I think that this is what you need:
Check entity:
entity = Entity.objects.filter(active=True, key__is_valid=True, user=request.user)
Check Profile
profile = Profile.objects.filter(user=request.user, profile_id__isnull=False)

Categories