Subquery select where clause - django

I have this sql statement:
SELECT * FROM result WHERE bet_id IN (SELECT id FROM bet WHERE STATUS="Active")
and this is my view:
def manageresult(request):
if 'usid' in request.session:
result = Result.objects.all()
admin = Admin.objects.get(id=request.session['usid'])
return render(request, 'manageresult.html', {'result':result,'admin':admin})
else:
return redirect('login')
How to change result = Result.objects.all() to that sql statement?
This is bet model:
class Bet(models.Model):
status = models.CharField(max_length=20, default="Active")
This is Result model:
class Result(models.Model):
bet = models.OneToOneField(Bet, on_delete=models.CASCADE)

disclaimer: when writing this answer, the models in question were not known
In case the Result model has a ForeignKey to Bet, you can filter by joins - it would make it more like
result = Result.objects.filter(bet__status='Active')
which would translate to the below SQL query
SELECT result.* FROM result INNER JOIN bet on result.bet_id = bet.id WHERE bet.STATUS="Active"
See Django's documentation on Lookups that span relationships
If that is not the case, Todor's answer is the way to go

You can use bet__status=...:
result = Result.objects.filter(bet__status='Active')

You can make querysets nested
result = Result.objects.filter(bet__in=Bet.objects.filter(status='active'))

Related

How to query on OneToOne relation of a related_name object in django

I have this model:
class ProgramRequirement(Model):
program = OneToOneField(Program, related_name='program_requirement')
prereq_program = ForeignKey(Program, related_name='prereq_program_requirement')
is_english_required = BooleanField()
and this model
class Program(Model):
field_1 = ...
field_3 = ...
I need to write a query that would return the primary key of the programs of which is_english_required of the prereq_program is True.
I tried this but it seems to be a wrong query:
ProgramRequirement.objects.filter(prereq_program__prereq_program_requirement__is_english_required =True).values_list('program__pk', flat=True)
However, it is not returning the correct result.
I am not sure if it is what I want but I am also thinking of this:
Program.objects.filter(prereq_program_requirement__is_english_required =True).values_lis('pk', flat=True)
Any idea of how to retrieve do the abovementioned result?
You might try:
ProgramRequirement.objects.filter(prereq_program__programrequirement__is_english_required = True).values('pk')

how to select boolean fields in django model

I have 2 models in different apps:
class Stock(models.Model):
vsej_seti = models.BooleanField(default=False, verbose_name=_('Все сети'))
and
class Hotel(ServioResource):
stock_all = models.ForeignKey('content.Stock', related_name='st', null=True, blank=True)
Please help me to write a method which sort all booleanfields with true parametr. In sql it looks like "SELECT * FROM content_stock WHERE vsej_seti=1". I wrote smth like this, but it doesn't work. Thanks
def qqq(self):
f = False
if self.stock_all.vsej_seti == f:
return self.stock_all.vsej_seti
You can just use a queryset filter
def qqq(self):
return self.stock_all.filter(vsej_seti=True)
Note: There may be more efficient queries avaiable depending on your use case but this is easily modified as per the docs

Rewrite raw SQL as Django query

I am trying to write this raw SQL query,
info_model = list(InfoModel.objects.raw('SELECT *,
max(date),
count(postid) AS freq,
count(DISTINCT author) AS contributors FROM
crudapp_infomodel GROUP BY topicid ORDER BY date DESC'))
as a django query. The following attempt does not work as I can't get related fields for 'author' and 'post'.
info_model = InfoModel.objects.values('topic')
.annotate( max=Max('date'),
freq=Count('postid'),
contributors=Count('author',
distinct=True))
.order_by('-max')
With raw SQL I can use SELECT * but how can I do the equivalent with the Django query?
The model is,
class InfoModel(models.Model):
topicid = models.IntegerField(default=0)
postid = models.IntegerField(default=0)
author = models.CharField(max_length=30)
post = models.CharField(max_length=30)
date = models.DateTimeField('date published')
I did previously post this problem here Django Using order_by with .annotate() and getting related field
I guess you want to order by the maximum date so:
InfoModel.objects.values('topic')
.annotate(
max=Max('date'), freq=Count('postid'),
contributors=Count('author', distinct=True))
.order_by('max')
The following view amalgamates two queries to solve the problem,
def info(request):
info_model = InfoModel.objects.values('topic')
.annotate( max=Max('date'),
freq=Count('postid'),
contributors=Count('author', distinct=True))
.order_by('-max')
info2 = InfoModel.objects.all()
columnlist = []
for item in info2:
columnlist.append([item])
for item in info_model:
for i in range(len(columnlist)):
if item['max'] == columnlist[i][0].date:
item['author'] = columnlist[i][0].author
item['post'] = columnlist[i][0].post
return render(request, 'info.html', {'info_model': info_model})

Adding aggregate over filtered self-join field to Admin list_display

I would like to augment one of my model admins with an interesting value. Given a model like this:
class Participant(models.Model):
pass
class Registration(models.Model):
participant = models.ForeignKey(Participant)
is_going = models.BooleanField(verbose_name='Is going')
Now, I would like to show the number of other Registrations for this Participant where is_going is False. So, something akin to this SQL query:
SELECT reg.*, COUNT(past.id) AS not_going_num
FROM registrations AS reg, registrations AS past
WHERE past.participant_id = reg.participant_id AND
past.is_going = False
I think I can extend the Admin's queryset() method according to Django Admin, Show Aggregate Values From Related Model, by annotating it with the extra Count, but I still cannot figure out how to work the self-join and filter into this.
I looked at Self join with django ORM and Django self join , How to convert this query to ORM query, but the former is doing SELECT * AND the latter seems to have data model problems.
Any suggestions on how to solve this?
See edit history for previous version of the answer.
The admin implementation below will display "Not Going Count" for each Registration model. The "Not Going Count" is the count of is_going=False for the registration's participant.
#admin.register(Registration)
class RegistrationAdmin(admin.ModelAdmin):
list_display = ['id', 'participant', 'is_going', 'ng_count']
def ng_count(self, obj):
return obj.not_going_count
ng_count.short_description = 'Not Going Count'
def get_queryset(self, request):
qs = super(RegistrationAdmin, self).get_queryset(request)
qs = qs.filter(participant__registration__isnull=False)
qs = qs.annotate(not_going_count=Sum(
Case(
When(participant__registration__is_going=False, then=1),
default=0,
output_field=models.IntegerField())
))
return qs
Below is a more thorough explanation of the QuerySet:
qs = qs.filter(participant__registration__isnull=False)
The filter causes Django to perform two joins - an INNER JOIN to participant table, and a LEFT OUTER JOIN to registration table.
qs = qs.annotate(not_going_count=Sum(
Case(
When(participant__registration__is_going=False, then=1),
default=0,
output_field=models.IntegerField())
)
))
This is a standard aggregate, which will be used to SUM up the count of is_going=False. This translates into the SQL
SUM(CASE WHEN past."is_going" = False THEN 1 ELSE 0 END)
The sum is generated for each registration model, and the sum belongs to the registration's participant.
I might misunderstood, but you can do for single participant:
participant = Participant.objects.get(id=1)
not_going_count = Registration.objects.filter(participant=participant,
is_going=False).count()
For all participants,
from django.db.models import Count
Registration.objects.filter(is_going=False).values('participant') \
.annotate(not_going_num=Count('participant'))
Django doc about aggregating for each item in a queryset.

Reducing queries for manytomany models in django

EDIT:
It turns out the real question is - how do I get select_related to follow the m2m relationships I have defined? Those are the ones that are taxing my system. Any ideas?
I have two classes for my django app. The first (Item class) describes an item along with some functions that return information about the item. The second class (Itemlist class) takes a list of these items and then does some processing on them to return different values. The problem I'm having is that returning a list of items from Itemlist is taking a ton of queries, and I'm not sure where they're coming from.
class Item(models.Model):
# for archiving purposes
archive_id = models.IntegerField()
users = models.ManyToManyField(User, through='User_item_rel',
related_name='users_set')
# for many to one relationship (tags)
tag = models.ForeignKey(Tag)
sub_tag = models.CharField(default='',max_length=40)
name = models.CharField(max_length=40)
purch_date = models.DateField(default=datetime.datetime.now())
date_edited = models.DateTimeField(auto_now_add=True)
price = models.DecimalField(max_digits=6, decimal_places=2)
buyer = models.ManyToManyField(User, through='Buyer_item_rel',
related_name='buyers_set')
comments = models.CharField(default='',max_length=400)
house_id = models.IntegerField()
class Meta:
ordering = ['-purch_date']
def shortDisplayBuyers(self):
if len(self.buyer_item_rel_set.all()) != 1:
return "multiple buyers"
else:
return self.buyer_item_rel_set.all()[0].buyer.name
def listBuyers(self):
return self.buyer_item_rel_set.all()
def listUsers(self):
return self.user_item_rel_set.all()
def tag_name(self):
return self.tag
def sub_tag_name(self):
return self.sub_tag
def __unicode__(self):
return self.name
and the second class:
class Item_list:
def __init__(self, list = None, house_id = None, user_id = None,
archive_id = None, houseMode = 0):
self.list = list
self.house_id = house_id
self.uid = int(user_id)
self.archive_id = archive_id
self.gen_balancing_transactions()
self.houseMode = houseMode
def ret_list(self):
return self.list
So after I construct Itemlist with a large list of items, Itemlist.ret_list() takes up to 800 queries for 25 items. What can I do to fix this?
Try using select_related
As per a question I asked here
Dan is right in telling you to use select_related.
select_related can be read about here.
What it does is return in the same query data for the main object in your queryset and the model or fields specified in the select_related clause.
So, instead of a query like:
select * from item
followed by several queries like this every time you access one of the item_list objects:
select * from item_list where item_id = <one of the items for the query above>
the ORM will generate a query like:
select item.*, item_list.*
from item a join item_list b
where item a.id = b.item_id
In other words: it will hit the database once for all the data.
You probably want to use prefetch_related
Works similarly to select_related, but can deal with relations selected_related cannot. The join happens in python, but I've found it to be more efficient for this kind of work than the large # of queries.
Related reading on the subject