I am new to Django and came up with a problem. I have a table like
TABLE
Id Title Type Value
1 A 1 10
2 A 1 20
3 A 2 5
4 A 2 8
5 B 1 1000
Model
class Company(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
value = models.IntegerField()
type = models.SmallIntegerField(default='1')
date = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE)
Now from the above table how can I get a value where I sum the value of a similar type and subtract the value from the different type ((A+A)-(A+A)) = 17). Also, I have a value of two types only.
How can I write the query to get the result.
You can aggregate over the model with:
from django.db.models import Q, Sum, Value
from django.db.models.functions import Coalesce
Company.objects.aggregate(
total=(Coalesce(Sum('value', filter=Q(type=1)), Value(0)) -
Coalesce(Sum('value', filter=Q(type=2)), Value(0)))
)['total']
Related
The output could look like this, for example:
id
secondary_id
fk
1
1
1
2
2
1
3
3
1
4
1
2
5
2
2
For context:
(see models below)
I have a commission structure which will have brackets depending on how much a user is earning in a month.
Ideally, I need to know in my Commission Bracket model, the bracket index for a given structure.
Here are my models.
class CommissionStructure(APIBaseModel):
advisor = models.ManyToManyField(AdviserDetail)
name = models.CharField(max_length=20, blank=True, null=True, default='default')
company = models.ForeignKey(Company, on_delete=models.CASCADE)
start_dt = models.DateTimeField(auto_now_add=True)
end_dt = models.DateTimeField(default=timezone.datetime.max)
objects = CommissionStructureManager()
class CommissionBracket(APIBaseModel):
<secondary_id ???>
commission_structure = models.ForeignKey(CommissionStructure, on_delete=models.CASCADE, related_name="brackets")
lower_bound = models.DecimalField(decimal_places=2, default=0.00, max_digits=20, null=True, blank=True)
upper_bound = models.DecimalField(decimal_places=2, default=0.00, max_digits=20, null=True, blank=True)
Please note, I may not have to store it on my model if I can add an annotation to an aggregate set, but my preference is to follow DRY.
Thank you
My suggestion would be to execute custom SQL directly. You can add the secondary id as an integer field in CommissionBracket. Then, you can implement this:
from django.db import connection
def sample_view(request):
...
with connection.cursor() as cursor:
cursor.execute('''
INSERT INTO appname_commissionbracket (
secondary_id,
commission_structure_id
)
SELECT CASE
WHEN MAX(secondary_id)
THEN MAX(secondary_id) + 1
ELSE 1
END AS new_secid, %s
FROM appname_commissionbracket
WHERE commission_structure_id = %s''',
[1, 1] # Sample foreign key value
)
return render(...)
Here we're using INSERT INTO SELECT since we're basing the new record's secondary_id from the same table. We're also adding a CASE so that we can have a fallback value if no record with commission_structure_id value as 1 is returned.
In case you need to populate other columns during create, you can simply include them like so:
INSERT INTO (secondary_id, commission_structure_id, lower_bound, upper_bound)
SELECT CASE ... END AS new_secid, <fk_value>, <lower_bound_value>, <upper_bound_value>
I've found a way to annotate the queryset, but for interest, my original question still remains: how do I add another field partitioned by the foreign key?
brackets = CommissionBracket.objects.select_related("commission_structure")\
.prefetch_related(
'commission_structure__advisor',
'commission_structure__start_dt__gte',
'commission_structure__end_dt__lte',
'commission_structure__company',
'bracket_values'
).filter(
commission_structure__advisor=advisor,
commission_structure__start_dt__lte=date,
commission_structure__end_dt__gte=date,
commission_structure__company=advisor.user.company,
).annotate(index=Window(
expression=Count('id'),
partition_by="commission_structure",
order_by=F("lower_bound").asc()))
Model:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=100)
class Result(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
outcome = models.IntegerField()
time = models.DateTimeField()
Sql:
select * from person as p
inner join (
select person_id, max(time) as max_time, outcome from result
group by person_id
) r on p.id = r.person_id
where r.result in (2, 3)
I'm wanting to get the all person records where the last result outcome was either a 2 or 3. I added the raw sql above to further explain.
I looked at using a subquery to filter person records that have a matching result id
sub_query = Result.objects.values("person_id").annotate(max_time=Max("time"))
however using values strips out the other fields.
Ideally I'd be able to do this in one person queryset but I don't think that is the case.
The below query exclude all persons whose marks not equal to 2 OR 3, then it sorts the results in time descending order (latest will be on top) and finally get the details for person ...
from django.db.models import Q
results = Results.objects.filter(Q(outcome=3) | Q(outcome=2)).order_by('-time').values('person')
As a person may have multiple result records and I only want to check the last record, A subquery was the only way I could find to do this
last_result = Subquery(
Result.objects.filter(person_id=OuterRef("pk")).order_by("-time").values("result")[:1]
)
people = Person.objects.all().annotate(max_time=Max("result__time"), current_result=last_result).filter(current_result__in=[2,3)
First I create a sub query that will return the last result record. Then I add this as a field in the people query so that I can filter on that for only results with 2 or 3.
This was it will only return person records where the current result is a 2 or 3.
I have following models:
class Topic(models.Model):
title = models.CharField(max_lenght=32)
# Some other fields.
class Thread(models.Model):
topic = models.ForeignKey(Topic, related_name=threads', on_delete=models.CASCADE)
# some other fields
class Message(models.Model):
thread = models.ForeignKey(Thread, related_name='messages', on_delete=models.CASCADE)
text = models.TextField()
created = models.DateTimeField(auto_now_add=True)
I want to calculate index of each element in each Thread, in the queryset by F(). For example If in the Thread 1 I have 5 messages, I want to messages have indexes as 1 to 5.
my code doesn't work.
code comes in the following:
from django.models import Count, Q, F
messages = Message.objects.filter(...).annotate(
index=Count('id', filter=Q(thread_id=F('thread_id'), created__lt=F('created'))) + 1
).annotate(
page=F('index') / 20
)
This return same index for all elements. For example this returns index=5 for all items in queryset.
How can I calculate index of elements in the queryset?
UPDATE:
Consider following:
I have 1000 Messages.
page size = 20.
number of messages pages = 1000 / 20 = 50
Now if I filter searched = Message.objects.filter(text__contains="hello"), 5 messages will be returned in the searched queryset.
My ultimate goal is to find each message is in which page? (I have 50 pages)
If you want this done by the database, you want to look into Window functions, specifically the Rank function (https://docs.djangoproject.com/en/3.2/ref/models/database-functions/#rank), which allows you to assign each row a number according to a grouping (like by thread)
This might do the trick:
from django.db.models import Window, F
from django.db.models.functions import Rank
index = Window(
expression=Rank(),
order_by=F('created'),
partition_by=F('thread'),
)
Message.objects.annotate(index=index)
I have the following models:
class User(models.Model):
id = models.CharField(max_length=10, primary_key=True)
class Data(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
timestamp = models.IntegerField(default=0)
Given a single user, I would like to know how I can filter using timestamp. For example:
Obtain the data from user1, between now and 1 hour ago.
I have the current timestamp with now = time.time(), also I have 1 hour ago using hour_ago = now-3600
I would like to obtain the Data that has a timestamp between these two values.
Use range to obtain data between two values.
You can use range anywhere you can use BETWEEN in SQL — for dates, numbers and even characters.
e.g.
Data.objects.filter(timestamp__range=(start, end))
from docs:
import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
Entry.objects.filter(pub_date__range=(start_date, end_date))
You can use __gte which is termed as greater than or equal to and __lte refers less than or equal toSo try this,
Data.objects.filter(timestamp__gte=hour_ago,timestamp__lte=now)
You can find similar examples in official doc
Short description: given a queryset myQueryset, how do I select max("myfield") without actually retrieving all rows and doing max in python?
The best I can think of is max([r["myfield"] for r in myQueryset.values("myfield")]), which isn't very good if there are millions of rows.
Long description: Say I have two models in my Django app, City and Country. City has a foreign key field to Country:
class Country(models.Model):
name = models.CharField(max_length = 256)
class City(models.Model):
name = models.CharField(max_length = 256)
population = models.IntegerField()
country = models.ForeignKey(Country, related_name = 'cities')
This means that a Country instance has .cities available. Let's say I now want to write a method for Country called highest_city_population that returns the population of the largest city. Coming from a LINQ background, my natural instinct is to try myCountry.cities.max('population') or something like that, but this isn't possible.
Use Aggregation (new in Django 1.1). You use it like this:
>>> from django.db.models import Max
>>> City.objects.all().aggregate(Max('population'))
{'population__max': 28025000}
To get the highest population of a City for each Country, I think you could do something like this:
>>> from django.db.models import Max
>>> Country.objects.annotate(highest_city_population = Max('city__population'))