Issue
I have a model field I am trying to update using Django's .update()
method. This field is a JsonField. I am typically saving a dictionary in this field. e.g. {"test": "yes", "prod": "no"}
Here is the model:
class Question(models.Model):
# {"test": "yes", "prod": "no"}
extra_data = models.JSONField(default=dict, null=True, blank=True)
I am able to update a key inside the dictionary using this query (which works fine by the way):
Question.filter(id="123").update(meta_data=Func(
F("extra_data"),
Value(["test"]),
Value("no", JSONField()),
function="jsonb_set",
))
The question now is, is there a way I can update the multiple keys at once(in my own case, two) using the .update()
as shown in the above query?
I want to use the .update()
method instead of .save()
to achieve this so I can avoid calling a post_save signal function.
Solution
Disclaimer: it's gonna look ugly.
I have done this in the past although in pure SQL, not using Django. The idea was to recursively call jsonb_set()
. Each call would set one key.
So in SQL, to set the keys {"test":"yes","prod":"no"}
you would do something like this:
UPDATE question SET meta_data = jsonb_set(jsonb_set(extra_data, '{test}', '"yes"'::jsonb), '{prod}', '"no"'::jsonb)
WHERE id = 123;
Please note that there are two nested calls to jsonb_set, the innermost using meta_data
, and the outermost receiving the result of the innermost.
So the Django equivalent would look something like this (beware, not tested):
Question.filter(id="123").update(
meta_data=Func(
Func(
F("extra_data"),
Value(["test"]),
Value("yes", JSONField()),
function="jsonb_set",
),
Value(["prod"]),
Value("no", JSONField()),
function="jsonb_set",
))
Or you could make a recursive function that returns that mess for you, setting one item at a time:
def recursive_jsonb_set(original, **kwargs):
if kwargs:
key, value = kwargs.popitem()
return Func(
recursive_jsonb_set(original, **kwargs),
Value(key.split('__')),
Value(value, JSONField()),
function="jsonb_set")
else:
return original
Question.filter(id="123").update(
meta_data=recursive_jsonb_set(F("extra_data"), test="yes", prod="no", other__status="done"))
Note the '__'
feature to update subkeys.
Answered By - augustomen
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.