I need to do a values/values_list query on nested key on a postgres backed jsonfield in django 1.10
eg.
class AbcModel(models.model):
context = fields.JSONField()
If it has values like:
{
'lev1': {
'lev': 2
}
}
I want to run a queries like
AbcModel.objects.values('context__lev1__lev2').distinct()
AbcModel.objects.values_list('context__lev1__lev2', flat=True).distinct()
EDIT: The JSON fields are the official django JSONField from django.contrib.postgres.fields
So I found a solution, this works with django 1.10 and above.
I used the KeyTransform to annotate and extract the nexted key and did a values_list on that.
from django.contrib.postgres.fields.jsonb import KeyTransform
extracted_query = AbcModel.objects.annotate(lev1=KeyTransform('lev1', 'context')).annotate(lev2=KeyTransform('lev', 'lev1'))
This query allows me to use lev1 and lev2 as normal fields in the model, so I can do a values, values_list or any other valid query on the fields.
Django 1.11 allows to nest the the two Transforms in one annotate, not sure about 1.10 about the nesting as I have upgraded to 1.11
It's not ideal, but I was able to get this working by adding the json field as an extra field and then calling values on that extra field:
AbcModel.objects.extra(select={
"extra_field": "context->'lev1'->'lev2'"
}).values('extra_field').distinct()
AbcModel.objects.extra(select={
"extra_field": "context->'lev1'->'lev2'"
}).values_list('extra_field', flat=True).distinct()
Related
Using a Django JSONField on PostgreSQL, say I have the following model
class Foo(models.Model):
bar = models.JSONField(null=False)
and I want to set a row to have bar=null where null is a single JSON null value (different to a database NULL value).
I can achieve it through a raw SQL query:
UPDATE "myapp_foo"
SET "bar" = 'null'::jsonb
WHERE "myapp_foo"."id" = <relevant_id>
The closest things I can do in the ORM is
Foo.objects.filter(id=<relevant_id>).update(bar=None)
but Django is interpreting this as a database NULL and I get the following error:
null value in column "bar" violates not-null constraint
or
Foo.objects.filter(id=<relevant_id>).update(bar="null")
but Django (correctly) interprets this as the JSON string "null"
Am I missing something or is this a gap in the ORM functionality?
Update: The Django docs have details about how to do this: https://docs.djangoproject.com/en/dev/topics/db/queries/#storing-and-querying-for-none
I have worked out this can be done with the Django Value class.
from django.db.models import Value
Foo.objects.filter(id=<relevant_id>).update(bar=Value("null"))
I have an ordinary ViewSet that fetch data from PostgesDB, applies some BackEnd filters etc.
All of the processing works thanks to the fact that Notes.objects.all() returns a QuerySet.
How do I turn a free Elasticsearch query result to QuerySet?
Alternatively, How do I turn a Dict into QuerySet?
Thank in advance.
For those coming from google, this is how I achieved it, using the django-elasticsearch-dsl package.
fields = ['title', 'description']
s = ItemDocument.search().query("multi_match", fields=fields, fuzziness='AUTO', query='sutures').exclude("match", is_latest=True).highlight('title')
s.to_queryset() # this will return a queryset
I'm using Django 1.9 and Postgres 9.5. I have a model (MyModel) with a JSONField (django.contrib.postgres.fields). The JSON value has the same structure in all the objects in the model.
{
"key1": int_val_1,
"key2": int_val_2
}
I want to order my queryset by a value of a specified key in my JSONField. Something like
MyModel.objects.annotate(val=F('jsonfield__key1')).order_by('val')
This does not work - i get the following error
Cannot resolve keyword 'key1' into field. Join on 'jsonfield' not permitted.
Is there anyway i can achieve this?
for Django >= 1.11, you can use:
from django.contrib.postgres.fields.jsonb import KeyTextTransform
MyModel.objects.annotate(val=KeyTextTransform('key1', 'jsonfield')).order_by('val')
An updated answer for this question:
At current time of writing (Django 3.1), you can now order exactly as you had hoped without needing any helper functions:
MyModel.objects.order_by('jsonfield__key1')
JSONExtract is use with mysql
Syntax ('json-field-name', 'json-field-key')
~Q is used for "not equal"
from django_mysql.models.functions import JSONExtract
Listing.objects.annotate(weight=JSONExtract('package_dimensions','$.p>ackage_weight')).filter(~Q(weight=0))
I'm using json field on my django model:
class JsonTable(models.Model):
data = JSONField()
type = models.IntegerField()
I tried next query, which works for normal sql fields:
JsonTable.objects.filter(type=1).values('type').annotate(Avg('data__superkey'))
But this throws next error:
FieldError: Cannot resolve keyword 'superkey' into field. Join on 'data' not permitted.
Is there way to make group by on json key, using Django ORM or some python lib, without use of raw sql?
Versions: Django 1.9b, PostgreSQL 9.4
UPDATE
Example 2:
JsonTable.objects.filter(type=1).values('data__happykey').annotate(Avg('data_superkey'))
throws same error on happykey
After some researching I found next solution:
from django.db.models import Count
from django.contrib.postgres.fields.jsonb import KeyTextTransform
superkey = KeyTextTransform('superkey', 'data')
table_items = JsonTable.objects.annotate(superkey = superkey).values('superkey').annotate(Count('id')).order_by()
I did not sure about order_by(), but documentation says that is needed.
For another aggregation function type casting needed:
from django.db.models import IntegerField
from django.db.models.functions import Cast
superkey = Cast(KeyTextTransform('superkey', 'data'), IntegerField())
I test with another model, hope that write this code without misprints. PostgreSQL 9.6, Django 2.07
If you are using this package https://github.com/bradjasper/django-jsonfield,
there is nothing in the code for managing such simulated related queries (data__some_json_key)
As Json data is text, you will have to go to raw sql or better : use queryset extra() method, but parsing Json in sql seems to be difficult.
I have a model with a text field on it. I want to do a lookup that will return all the items that have a string with a length of 7 or more in that field. Possible?
How about a lookup for all objects in which that field isn't ''?
I think regex lookup can help you:
ModelWithTextField.objects.filter(text_field__iregex=r'^.{7,}$')
or you can always perform raw SQL queries on Django model:
ModelWithTextField.objects.raw('SELECT * FROM model_with_text_field WHERE LEN_FUNC_NAME(text_field) > 7')
where len_func_name is the name of "string length" function for your DBMS. For example in mysql it's named "length".
Since django 1.8, you can use the Length database function:
from django.db.models.functions import Length
qs = ModelWithTextField.objects \
.annotate(text_len=Length('text_field_name')) \
.filter(text_len__gte=7)
Since Django 1.9 the Length database function may be registered as a queryset lookup:
from django.db.models import CharField
from django.db.models.functions import Length
CharField.register_lookup(Length, 'length')
So that then you are able use it in a following manner:
# Get authors whose name is longer than 7 characters
authors = Author.objects.filter(name__length__gt=7)
See: Database Functions - Length() in official Django 3.2 Docs