#JSONField vs. Aggregation

15 messages · Page 1 of 1 (latest)

mighty basin
#

Hi, I have a django.db.models.JSONField called content holding an object that I "index" into using the syntax "content__field", where the field is a JSON number. I am trying to use this in a django.db.models.Sum, as in django.db.models.Sum("content__field"), but I get this error, which I am wondering is a known quirk:

TypeError: the JSON object must be str, bytes or bytearray, not float

This is traced back to the call to json.loads in django/db/models/fields/json.py's from_db_value. It appears (based on local modifications) that Django is passing in an already-python-native value to json.loads, which is in a try. I suspect that the subsequent except was sort of meant to handle this situation, but that only catches a json.JSONDecodeError, not a TypeError. If I change that except to a catch-all, the aggregate returns the correct value.

Is this a bug? Am I doing something wrong here?

queen agate
#

You need to use a KeyTransform (or KT) and cast to a number. For example:

from django.db.models.fields.json import KT
from django.db.models.functions import Cast

Foo.objects.aggregate(
    total_field=Sum(Cast(KT('content__field'), output_field=IntegerField()))
)
#

P.S. There are a whole lot of notes on KT here

mighty basin
#

Thanks. Yeah, I saw all of these and I've read through the vast majority of Django documentation, but it doesn't really say why this is necessary, at least in a manner that's clear, IMO (especially when you don't need them to filter). I'll keep looking to try to understand, but I don't know if I am going to be successful here.

#

For example, I don't understand why F can't do the job of KT in the example given on that webpage.

queen agate
#

As in, you're expecting the usage of content__field to work just about the same via Sum as it did with F ?

mighty basin
#

Yeah I just don't understand why JSON would necessitate a difference; why couldn't F special case JSON so that we don't have to be concerned about the difference?

queen agate
#

I cannot say for sure sorry.

mighty basin
#

OK, no worries. Thanks for the help. I think I just don't understand what KT does that enables JSON usage compared to F

queen agate
#

Just gave it a concrete try, it does work.

Post.objects.create(content={'field': 1})
Post.objects.create(content={'field': 2})
Post.objects.create(content={'field': 3})

In [8]: Post.objects.aggregate(
   ...:     total_field=Sum(Cast(KT('content__field'), output_field=IntegerField()))
   ...: )
Out[8]: {'total_field': 6}
mighty basin
#

Other that that KT is meant for JSON, but that doesn't explain why

#

Are you using some REPL that doesn't require a full startproject? I was wondering if there was something that exists like that so I can test things out easily.

queen agate
#

No, I just have one project always ready to be tested on.

mighty basin
#

I see, makes sense

#

Appreciate the help