[Django] #35071: Resolve lazy translation objects inside dictionaries when saving JSONFields

12 views
Skip to first unread message

Django

unread,
Dec 29, 2023, 2:05:24 PM12/29/23
to django-...@googlegroups.com
#35071: Resolve lazy translation objects inside dictionaries when saving JSONFields
-------------------------------------+-------------------------------------
Reporter: Jacob | Owner: nobody
Walls |
Type: New | Status: new
feature |
Component: Database | Version: 4.2
layer (models, ORM) |
Severity: Normal | Keywords:
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
I have a model `Notification` like this:

{{{
class Notification(models.Model):
message = models.TextField(null=True)
context = models.JSONField(null=True)
}}}

Here is the lazy object...
{{{
>>> from django.utils.translation import gettext_lazy as _
>>> lazy = _("lazy")
}}}

... that I can use in a TextField ...

{{{
>>> models.Notification(message=lazy).save()
>>>
}}}

... but not for the JSONField without string-ifying it myself. Suggesting
an implementation that resolves these in one of the methods that prepares
values for database insertion. Tested with psycopg2 2.9.9.
{{{
>>> models.Notification(context={'lazy': lazy}).save()
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/jwalls/venv311/lib/python3.11/site-
packages/django/db/models/base.py", line 814, in save
self.save_base(
File "/Users/jwalls/venv311/lib/python3.11/site-
packages/django/db/models/base.py", line 877, in save_base
updated = self._save_table(
^^^^^^^^^^^^^^^^^
File "/Users/jwalls/venv311/lib/python3.11/site-
packages/django/db/models/base.py", line 990, in _save_table
updated = self._do_update(
^^^^^^^^^^^^^^^^
File "/Users/jwalls/venv311/lib/python3.11/site-
packages/django/db/models/base.py", line 1054, in _do_update
return filtered._update(values) > 0
^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jwalls/venv311/lib/python3.11/site-
packages/django/db/models/query.py", line 1231, in _update
return query.get_compiler(self.db).execute_sql(CURSOR)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jwalls/venv311/lib/python3.11/site-
packages/django/db/models/sql/compiler.py", line 1982, in execute_sql
cursor = super().execute_sql(result_type)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jwalls/venv311/lib/python3.11/site-
packages/django/db/models/sql/compiler.py", line 1560, in execute_sql
cursor.execute(sql, params)
File "/Users/jwalls/venv311/lib/python3.11/site-
packages/django/db/backends/utils.py", line 102, in execute
return super().execute(sql, params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jwalls/venv311/lib/python3.11/site-
packages/django/db/backends/utils.py", line 67, in execute
return self._execute_with_wrappers(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jwalls/venv311/lib/python3.11/site-
packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
return executor(sql, params, many, context)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jwalls/venv311/lib/python3.11/site-
packages/django/db/backends/utils.py", line 89, in _execute
return self.cursor.execute(sql, params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jwalls/venv311/lib/python3.11/site-
packages/psycopg2/_json.py", line 78, in getquoted
s = self.dumps(self.adapted)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jwalls/venv311/lib/python3.11/site-
packages/psycopg2/_json.py", line 72, in dumps
return self._dumps(obj)
^^^^^^^^^^^^^^^^
File
"/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/json/__init__.py",
line 231, in dumps
return _default_encoder.encode(obj)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File
"/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/json/encoder.py",
line 200, in encode
chunks = self.iterencode(o, _one_shot=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File
"/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/json/encoder.py",
line 258, in iterencode
return _iterencode(o, 0)
^^^^^^^^^^^^^^^^^
File
"/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/json/encoder.py",
line 180, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type __proxy__ is not JSON serializable
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/35071>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Dec 31, 2023, 3:29:12 AM12/31/23
to django-...@googlegroups.com
#35071: Resolve lazy translation objects inside dictionaries when saving JSONFields
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: nobody
Type: New feature | Status: closed
Component: Database layer | Version: 4.2
(models, ORM) |
Severity: Normal | Resolution: wontfix
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Mariusz Felisiak):

* status: new => closed
* resolution: => wontfix


Comment:

It's easier to resolve a type for string-based fields, however,
`JSONField`'s values can contain different types of data. I don't think
it's worth adding extra guess-like complexity for this. We can reconsider
this decision if you provide a PoC.

--
Ticket URL: <https://code.djangoproject.com/ticket/35071#comment:1>

Django

unread,
Dec 31, 2023, 8:54:51 AM12/31/23
to django-...@googlegroups.com
#35071: Resolve lazy translation objects inside dictionaries when saving JSONFields
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: nobody
Type: New feature | Status: closed
Component: Database layer | Version: 4.2
(models, ORM) |
Severity: Normal | Resolution: wontfix
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Jacob Walls):

I was thinking of something like this:

{{{#!diff
diff --git a/django/db/models/fields/json.py
b/django/db/models/fields/json.py
index 7b9b2ae6b2..2df4f8ee12 100644
--- a/django/db/models/fields/json.py
+++ b/django/db/models/fields/json.py
@@ -1,6 +1,8 @@
+from collections.abc import Container
import json

from django import forms
+from django.conf import settings
from django.core import checks, exceptions
from django.db import NotSupportedError, connections, router
from django.db.models import expressions, lookups
@@ -11,6 +13,7 @@ from django.db.models.lookups import (
PostgresOperatorLookup,
Transform,
)
+from django.utils.functional import Promise


from django.utils.translation import gettext_lazy as _

from . import Field
@@ -96,6 +99,21 @@ class JSONField(CheckFieldDefaultMixin, Field):
def get_internal_type(self):
return "JSONField"

+ def get_prep_value(self, value):
+ if settings.USE_I18N:
+ match value:
+ case dict():
+ value = {
+ self.get_prep_value(k): self.get_prep_value(v)
+ for k, v in value.items()
+ }
+ case str() | Promise():
+ pass
+ case Container():
+ value = value.__class__(self.get_prep_value(v) for v
in value)
+
+ return super().get_prep_value(value)
+
def get_db_prep_value(self, value, connection, prepared=False):
if not prepared:
value = self.get_prep_value(value)

}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/35071#comment:2>

Django

unread,
Dec 31, 2023, 9:15:22 AM12/31/23
to django-...@googlegroups.com
#35071: Resolve lazy translation objects inside dictionaries when saving JSONFields
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: nobody
Type: New feature | Status: closed
Component: Database layer | Version: 4.2
(models, ORM) |
Severity: Normal | Resolution: wontfix
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Mariusz Felisiak):

Why do you want to put it behind the `USE_I18N`?

--
Ticket URL: <https://code.djangoproject.com/ticket/35071#comment:3>

Django

unread,
Dec 31, 2023, 9:26:34 AM12/31/23
to django-...@googlegroups.com
#35071: Resolve lazy translation objects inside dictionaries when saving JSONFields
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: nobody
Type: New feature | Status: closed
Component: Database layer | Version: 4.2
(models, ORM) |
Severity: Normal | Resolution: wontfix
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Jacob Walls):

Yeah, I hadn't yet looked into whether lazy translation objects get
resolved when USE_I18N = False. Looks like they do, so I should remove the
guard.

--
Ticket URL: <https://code.djangoproject.com/ticket/35071#comment:4>

Django

unread,
Dec 31, 2023, 9:43:24 AM12/31/23
to django-...@googlegroups.com
#35071: Resolve lazy translation objects inside dictionaries when saving JSONFields
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: nobody
Type: New feature | Status: new

Component: Database layer | Version: 4.2
(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Mariusz Felisiak):

* status: closed => new
* resolution: wontfix =>
* stage: Unreviewed => Accepted


Comment:

Tentatively accepted.

--
Ticket URL: <https://code.djangoproject.com/ticket/35071#comment:5>

Django

unread,
Dec 31, 2023, 9:43:48 AM12/31/23
to django-...@googlegroups.com
#35071: Resolve lazy translation objects inside dictionaries when saving JSONFields
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Jacob
| Walls
Type: New feature | Status: assigned
Component: Database layer | Version: dev

(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Mariusz Felisiak):

* owner: nobody => Jacob Walls
* status: new => assigned
* version: 4.2 => dev


--
Ticket URL: <https://code.djangoproject.com/ticket/35071#comment:6>

Reply all
Reply to author
Forward
0 new messages