Django Queryset compare two different models with multiple rows - python

I have these two models that I would like to return the sum of. I get an database error about the subquery returning more than one row. What would be the best way to compare both without using a for statement?
AuthorizationT(models.Model)
ar_id = models.BigIntegerField(blank=True, null=True)
status_flag = models.BigIntegerField(blank=True, null=True)
BillT(models.Model)
paid_id = models.BigIntegerField(blank=True, null=True)
recvd = models.FloatField(blank=True, null=True)
Query I tried
paidbill= BillT.objects.values_list('paid_id', flat=true)
AuthorizationT.objects.values().filter(ar_id=paidbill, status_flag=0).aggregate(Sum('recvd'))
In SQL I know it would be
select sum(recvd) from authorization_t a, bill_t b where a.ar_billid0= b.paid_id and a.status_flag=0
I'm looking for the equivalent in queryset

I think you won't be able to achieve without a for loop because I think you need to join the tables as there is a filtration on both tables and you want to sum a field from the first table. The way to join tables would be prefetch_related() or select_related() but they utilize foreign keys.
This leads me to a suggestion that the id fields: bill_id and ar_id should be normalized as it looks like there will be data duplication. Using a relationship would also make making queries simpler.

Since paidbill is a list, you have to use the __in suffix in your query:
AuthorizationT.objects.filter(ar_id__in=paidbill,status_flag=0).aggregate(Sum('recvd'))
If you model the relation of the models (ar_id, paid_id) via a ForeignKey or ManyToMany, you will be able to do this trivially in a single ORM statement

Related

Efficient Multiple Row and Columns Lookup Django

I need an efficient query that can enable lookup on multiple table rows and different columns. I have the below model:
class Vahala(models.Model):
tourist = models.ForeignKey(User)
name = models.CharField(max_length=100)
can_visit = models.BooleanField(default=False)
can_move_out = models.BooleanField(default=False)
I need to confirm if Mr A who is a tourist can visit a location named 'bali' and can also move out of a location named 'cape_verde'.
I want to believe the naive approach would be
check_a = Vahala.objects.filter(name='bali', can_visit=True, tourist__email='mra#mail.com')
check_b = Vahala.objects.filter(name='cape_verde', can_move_out=True,
tourist__email='mra#mail.com')
check_a and check_b must exists() before Mr A can complete the process.
I need an efficient approach. I don't want to keep hitting the database multiple times. Is it possible to confirm the conditions via a single DB hit or at most two if the conditions are much? What am I missing?
You can filter from the Users side and chain filters to make successive joins on a related model:
queryset = User.objects.filter(
email='mra#mail.com'
).filter(
vahala_set__name='bali',
vahala_set__can_visit=True
).filter(
vahala_set__name='cape_verde',
vahala_set__can_move_out=True
)
This will perform a join between User, Vahala and Vahala (twice, due to chained filter on the related model) and give you only users that match the given conditions.

Django models' best solution for this reverse ForeignKey

I'm making a personal project to manage restaurants.
Two of my models are facing a problem, these models are DiningRoom and Table.
DiningRoom is the representation of any area that the restaurant could have (e.g. we could have one area inside and other area in the terrace of the building).
And in every DiningRoom we can set a layout of Tables.
So, the more object-oriented way I find to map this is by many-to-one relationship (ForeignKey). Since one DiningRoom can have many Tables, and one Table can be only in one DiningRoom. Right?
So my models are:
class DiningRoom(models.Model):
account = models.ForeignKey(Account, on_delete=models.CASCADE, null=False)
name = models.CharField(max_length=50, null=False, blank=False)
rows = models.IntegerField(max=15, null=False)
cols = models.IntegerField(max=15, null=False) # rows and columns are for the room's grid layout.
class Table(models.Model):
row = models.IntegerField(max=15, null=False) # The row in the room's grid where the table is at.
col = models.IntegerField(max=15, null=False) # the column in the room's grid where the table is at.
dining_room = models.ForeignKey(DiningRoom, on_delete=models.CASCADE, null=False) # Here is the problem.
The problem is that when I am querying DiningRooms of the account in the view, I need to fetch also the Tables that are related to each DiningRoom in the queryset result.
def dining_rooms(request):
try:
account = Account.objects.get(id=request.session['account_id'])
except Account.DoesNotExists:
return response(request, "error.html", {'error': 'Account.DoesNotExists'})
dining_rooms = DiningRoom.objects.filter(account=account)
But also I need the Tables of the results in dining_rooms!
I found two possible solutions but none seem to be "correct" to me. One is to make a Many-to-many relationship and validate that any Table is only in one DiningRoom in the view. And the second and worse one could be fetching the Tables once for each DiningRoom obtained in the queryset (but imagine a restaurant with 5 or 6 different areas (DiningRooms), it would be needed to fetch the database six times for every time).
Doing it in vice-versa and fetching all the Tables and select_related DiningRooms is not possible since it's possible to have a DiningRoom with no Tables in it (and in this case we will have missing DiningRooms).
What could be the best way to handle this? Thanks!
You can use the related_name or relationships backwards, an acceptable solution would be to create a method in the DiningRoom model that is called associated_tables() and return using the related_name (modelname_set, in this case it would be table_set). It is the name of the lowercase child model followed by the suffix _set
class DiningRoom(models.Model):
#your fields
def associated_tables(self):
return self.table_set.all()
In addition, this video tutorial could clear your days and give you a better idea about reverse relationships:
https://youtu.be/7tAZdYRA8Sw

Django postgres order_by distinct on field

We have a limitation for order_by/distinct fields.
From the docs: "fields in order_by() must start with the fields in distinct(), in the same order"
Now here is the use case:
class Course(models.Model):
is_vip = models.BooleanField()
...
class CourseEvent(models.Model):
date = models.DateTimeField()
course = models.ForeignKey(Course)
The goal is to fetch the courses, ordered by nearest date but vip goes first.
The solution could look like this:
CourseEvent.objects.order_by('-course__is_vip', '-date',).distinct('course_id',).values_list('course')
But it causes an error since the limitation.
Yeah I understand why ordering is necessary when using distinct - we get the first row for each value of course_id so if we don't specify an order we would get some arbitrary row.
But what's the purpose of limiting order to the same field that we have distinct on?
If I change order_by to something like ('course_id', '-course__is_vip', 'date',) it would give me one row for course but the order of courses will have nothing in common with the goal.
Is there any way to bypass this limitation besides walking through the entire queryset and filtering it in a loop?
You can use a nested query using id__in. In the inner query you single out the distinct events and in the outer query you custom-order them:
CourseEvent.objects.filter(
id__in=CourseEvent.objects\
.order_by('course_id', '-date').distinct('course_id')
).order_by('-course__is_vip', '-date')
From the docs on distinct(*fields):
When you specify field names, you must provide an order_by() in the QuerySet, and the fields in order_by() must start with the fields in distinct(), in the same order.

Django filter 'first' lookup for many-to-many relationships

Let's say we have a two models like these:
Artist(models.Model):
name = models.CharField(max_length=50)
Track(models.Model):
title = models.CharField(max_length=50)
artist = models.ForeignKey(Artist, related_name='tracks')
How can I filter this relationship to get the first foreign record?
So I've tried something like this, but it didn't work (as expected)
artists = Artist.objects.filter(tracks__first__title=<some-title>)
artists = Artist.objects.filter(tracks[0]__title=<some-title>)
Is there any way to make this work?
Here's a solution not taking performance into consideration.
Artist.objects.filter(tracks__in=[a.tracks.first() for a in Artist.objects.all()], tracks__title=<some_title>)
No list approach, as requested.
Artist.objects.filter(tracks__in=Track.objects.all().distinct('artist').order_by('artist', 'id'), tracks__title=<some_title>)
The order_by 'id' is important to make sure distinct gets the first track based on insertion. The order_by 'artist' is a requirement for sorting distinct queries. Read about it here: https://www.postgresql.org/docs/9.0/static/sql-select.html#SQL-DISTINCT

How to compare Specific value of two different tables in django?

I have two tables 'Contact' and other is "Subscriber".. I want to Compare Contact_id of both and want to show only those Contact_id which is present in Contact but not in Subscriber.These two tables are in two different Models.
Something like this should work:
Contact.objects.exclude(
id__in=Subscriber.objects.all()
).values_list('id', flat=True)
Note that these are actually two SQL queries. I'm sure there are ways to optimize it, but this will usually work fine.
Also, the values_list has nothing to do with selecting the objects, it just modifies "format" of what is returned (list of IDs instead of queryset of objects - but same database records in both cases).
If you are excluding by some field other then Subscriber.id (e.g: Subscriber.quasy_id):
Contact.objects.exclude(
id__in=Subscriber.objects.all().values_list('quasy_id', flat=True)
).values_list('id', flat=True)
Edit:
This answer assumes you don't have a relationship between your Contact and Subscriber models. If you do, then see #navit's answer, it is a better choice.
Edit 2:
That flat=True inside exclude is actually not needed.
I assume you have your model like this:
class Subscriber(models.Model):
contact = models.ForeignKey(Contact)
You can do what you want like this:
my_list = Subscriber.objects.filter(contact=None)
This retrieves Subscribers which don't have a Contact. Retrieveing a list of Contacts is straightforward.
If you want to compare value of fields in two different tables(which have connection with ForeignKey) you can use something like this:
I assume model is like below:
class Contact(models.Model):
name = models.TextField()
family = models.TextField()
class Subscriber(models.Model):
subscriber_name = models.ForeignKey(Contact, on_delete=models.CASCADE)
subscriber_family = models.TextField()
this would be the query:
query = Subscriber.objects.filter(subscriber_name =F(Contact__name))
return query

Categories

Resources