I have the following model:
class Ticket(models.Model):
# ... other fields omitted
active_at = models.DateTimeField()
duration = models.DurationField()
Given now = datetime.now(), I'd like to retrieve all records for which now is between active_at and active_at + duration.
I'm using Django 1.8. Here are the DurationField docs.
As noted in the documentation, arithmetic with a DurationField will not always work as expected in databases other than PostgreSQL. I don't know to which extend this works or doesn't work in other databases, so you'll have to try it yourself.
If that works, you can use the following query:
from django.db.models import F
active_tickets = Ticket.objects.filter(active_at__lte=now,
active_at__gt=now-F('duration'))
The F object refers to a field in the database, duration in this case.
Related
I have these two models, Cases and Specialties, just like this:
class Case(models.Model):
...
judge = models.CharField()
....
class Specialty(models.Model):
name = models.CharField()
sys_num = models.IntegerField()
I know this sounds like a really weird structure but try to bare with me:
The field judge in the Case model refer to a Specialty instance sys_num value (judge is a charfield but it will always carries an integer) (each Specialty instance has a unique sys_num). So I can get the Specialty name related to a specific Case instance using something like this:
my_pk = #some number here...
my_case_judge = Case.objects.get(pk=my_pk).judge
my_specialty_name = Specialty.objects.get(sys_num=my_case_judge)
I know this sounds really weird but I can't change the underlying schemma of the tables, just work around it with sql and Django's orm.
My problem is: I want to annotate the Specialty names in a queryset of Cases that have already called values().
I only managed to get it working using Case and When but it's not dynamic. If I add more Specialty instances I'll have to manually alter the code.
cases.annotate(
specialty=Case(
When(judge=0, then=Value('name 0 goes here')),
When(judge=1, then=Value('name 1 goes here')),
When(judge=2, then=Value('name 2 goes here')),
When(judge=3, then=Value('name 3 goes here')),
...
Can this be done dynamically? I look trough django's query reference docs but couldn't produce a working solution with the tools specified there.
You can do this with a subquery expression:
from django.db.models import OuterRef, Subquery
Case.objects.annotate(
specialty=Subquery(
Specialty.objects.filter(sys_num=OuterRef('judge')).values('name')[:1]
)
)
For some databases, casting might even be necessary:
from django.db.models import IntegerField, OuterRef, Subquery
from django.db.models.functions import Cast
Case.objects.annotate(
specialty=Subquery(
Specialty.objects.filter(sys_num=Cast(
OuterRef('judge'),
output_field=IntegerField()
)).values('name')[:1]
)
)
But the modeling is very bad. Usually it is better to work with a ForeignKey, this will guarantee that the judge can only point to a valid case (so referential integrity), will create indexes on the fields, and it will also make the Django ORM more effective since it allows more advanced querying with (relativily) small queries.
I know this is not a django question per say but I am working with django models and would like to have a solution specific to django
Suppose I have a model like this
class Foo(models.Model):
type = models.IntegerField()
timestamp = models.DateTimeField(auto_now_add=True)
Now what is the best method get a count of all objects of type(say 1) spread over date/time
For example: get_stat(type=1) gives me information on how many objects(of type 1) were created on 12/10/2018, on 13/10/2018, 14/10/2018 and so on...
I think you need to use group by. See this answer: How to query as GROUP BY in django?
#classmethod
def get_stat(cls, type):
return cls.objects.filter(type=type).values('timestamp').annotate(
count=Count('id')
).values('timestamp', 'count')
This function is an example in your case.
I have 3 models similar to the below, and I am trying to get the latest sale date for my items in a single query, which is definitely possible using SQL, but I am trying to use the built in Django functionality:
class Item(models.Model):
name = models.CharField()
...
class InventoryEntry(models.Model):
delta = models.IntegerField()
item = models.ForeignKey("Item")
receipt = models.ForeignKey("Receipt", null=True)
created = models.DateTimeField(default=timezone.now)
...
class Receipt(models.Model):
amt = models.IntegerField()
...
What I am trying to do is query my items and annotate the last time a sale was made on them. The InventoryEntry model can be queried for whether or not an entry was a sale based on the existence of a receipt (inventory can also be adjusted because of an order, or being stolen, etc, and I am only interested in the most recent sale).
My query right now looks something like this, which currently just gets the latest of ANY inventory entry. I want to filter the annotation to only return the max value of created when receipt__isnull=False on the InventoryEntry:
Item.objects.filter(**item_qs_kwargs).annotate(latest_sale_date=Max('inventoryentry_set__created'))
I attempted to use the When query expression but it did not work as intended, so perhaps I misused it. Any insight would be appreciated
A solution with conditional expressions should work like this:
from django.db.models import Max, Case, When, F
sale_date = Case(When(
inventoryentry__receipt=None,
then=None
), default=F('inventoryentry__created'))
qs = Item.objects.annotate(latest_sale_date=Max(sale_date))
I have tried some modified solution. Have a look.
from django.db.models import F
Item.objects\
.annotate(latest_inventoryentry_id=Max('inventoryentry__created'))\
.filter(
inventoryentry__id=F('latest_inventoryentry_id'),
inventoryentry__receipt=None
)
I did not check manually. you can check and let me know.
Thanks
I want to create the following query in Django:
select field1, count(field1), log(count(field1)) from object_table
where parent_id = 12345
group by field1;
I've implemented field1, count(field1) and group by field1 by following:
from django.db.models import Count
Object.objects.filter(
parent = 12345
).values_list(
'field1'
).annotate(
count=Count('field1')
)
However if I add something like this
.extra(
select={'_log':'log(count)'}
)
it doesn't affect my results. Could you give me a clue what am I doing wrong? How to implement log(count(field)) within Django?
PS, I'm using Django 1.9.
Thanks in advance!
Note that some databases don't natively support logarithm function (e.g. sqlite). This is probably an operation that should be done in your Python code instead of the database query.
import math
for obj in object_list:
# use math.log() for natural logarithm
obj._log = math.log10(obj.count)
If you are certain you can rely on a database function and you want to use the database to perform the computation, you can use raw queries. For example, postgres has the log function implemented:
query = """\
select count(field1), log(count(field1)) as logvalue
from myapp_mymodel
group by field1"""
queryset = MyModel.objects.raw(query)
for obj in queryset:
print(obj.logvalue)
I have a model Django model like this:
class MyModel(models.Model):
fieldA = models.CharField(max_length=100)
fieldB = models.IntegerField()
fieldC = models.IntegerField()
I want to find the Max(fieldB + fieldC). Is there a Django way to do this? Or do I have to go with raw sql (in case it will be something like "Select Max(fieldA + fieldB) From mymodel_table")?
Thanks in advance
Here is a rather roundabout solution that should work, adapted from Django aggregate queries with expressions:
MyModel.objects.extra(select={'sum':'fieldB + fieldC'}).order_by('-sum')[0]
Original non-working answer (as of Django 1.4, F() expressions do not work in annotations)
You can use F() expressions and annotation/aggregation to do this for you. I think what you want is.
from django.db.models import F, Max
MyModel.objects.annotate(sum=F('fieldB') + F('fieldC')).aggregate(Max('sum'))