Django queryset - Isn’t it possible to filter on a

2019-06-03 11:12发布

问题:

I’m trying to do a very simple operation but I get an issue. I have a simple model :

class MyModel(models.Model):
    date = models.DateTimeField(null=False)
    value = models.FloatField(null=True)
    interval = models.IntegerField(null=True)

My goal is to get the MyModel object with the biggest value.

#Get the value
value = MyModel.objects.filter(date__gte=’2014-05-01’, date__lte=’2014-05-31’).aggregate(Max(‘value’))[‘value__max’]

# Get the object corresponding to the max value
my_object = MyModel.objects.get(value=value)

This code raise me an error : “matching query does not exist” … I tried to hardcode a value (that exists), I have the same error.

Am I doing something wrong ? Isn’t it possible to filter on a FloatField ?

Thanks.

回答1:

It is with the way floating point numbers are represented in Python (Django's FloatField maps to Python's float and Django's DecimalField maps to Python's decimal; see here).

The difference is that floats are not precise because of the way they are represented on your computer, the decimal type aims to remedy some of these issues that arise through that (have a look at the docs for it, they explain that very well).

Now given your case of trying to fetch a float via an exact match you can try 2 things:

  1. Change the type of value on MyModel to a DecimalField (i.e. value = models.DecimalField(max_digits=5, decimal_places=2, null=True))

  2. If you want to keep the type as a FloatField you can try to query a range of values, however you might end up with more than 1 result (so MyModel.objects.get() might throw an exception, complaining that the query returned more than 1 result): MyModel.objects.filter(value__range=(1.23, 1.24)) or MyModel.objects.filter(value__gte=1.23, value__lt=1.24)



回答2:

An easier way to get the object with the largest value would be to order it and take the first:

value = MyModel.objects.filter(date__gte='2014-05-01', date__lte='2014-05-31').order_by('-value')[0]

That has the advantage that it's a single query rather than 2, and doesn't incur an expensive aggregation.



回答3:

date field DateTimeField, but the code is passing strings. Specify datetime.date instead:

import datetime

...

value = MyModel.objects.filter(
    date__gte=datetime.date(2014, 5, 1),
    date__lte=datetime.date(2014, 5, 31)
).aggregate(Max('value'))['value__max']