Django ORM fails to generate valid sql for JSONb contains
lets start with the error first from my logs:
2025-10-21 19:18:11,380 ERROR api.services.observium_port_status_service Error getting port status from store: invalid input syntax for type json
LINE 1: ..." WHERE "api_jsonstatestore"."filter_key_json" @> '''{"type"...
^
DETAIL: Token "'" is invalid.
CONTEXT: JSON data, line 1: '...
and the query:
state = (
JsonStateStore.objects.select_for_update()
.filter(filter_key_json__contains={"type": "observium_port_status", "observium_port_id": observium_port_id})
.first()
)
here is an example record
id | created | touched | filter_key_json | data_json |
---|---|---|---|---|
33 | 2025-10-21 18:19:59.873 -0500 | 2025-10-21 18:44:57.047 -0500 | {"type": "observium_port_status", "observium_port_id": 987} | redacted |
and the model:
class JsonStateStore(models.Model):
created = models.DateTimeField(auto_now_add=True)
touched = models.DateTimeField(auto_now=True)
filter_key_json = models.JSONField()
data_json = models.JSONField()
class Meta:
verbose_name = "JSON State Store"
verbose_name_plural = "JSON State Stores"
def __str__(self):
return f"JsonStateStore(key={self.filter_key_json}, created={self.created}, touched={self.touched})"
def save(self, *args, **kwargs):
self.touched = timezone.now()
super().save(*args, **kwargs)
I am on django 4.2.2
my database backend is timescale.db.backends.postgis
(github | pypi) and i am on version 0.2.13
of that package
I cannot identify any syntax error in the QuerySet call and I cannot figure out what is going wrong here
My current workaround is
lock_sql = (
"""
SELECT id, created, touched, filter_key_json, data_json
FROM api_jsonstatestore
WHERE filter_key_json @> %s::jsonb
ORDER BY id ASC
LIMIT 1
FOR UPDATE
"""
)
payload = {"type": "observium_port_status", "observium_port_id": observium_port_id}
records = list(JsonStateStore.objects.raw(lock_sql, [json.dumps(payload)]))
state = records[0] if records else None
if anyone has any insight on why the ORM method does not work and would like to share I would appreciate it