Two-faced JSONField#
My team develops a rather complex Django service, that uses
django-jsonfield
in multiple places for some while.
Recently we added a new feature - sending invitation emails.
It was rather simple task, because we took magnificient django-post-office
.
Everything went fine, until we realized that unit tests for models,
that has JSONFields started to fail with cryptic errors.
Turned out, that django-post-office
depends on jsonfield
package.
And this package is slightly different from the one we already use.
They conflict, because both packages install into jsonfield
directory.
They have exactly the same public interface and are interchangeble in most cases.
And pip
does nothing to handle or detect this conflict and mixes both
packages into single directory producing unpredictable Frankenshtein.
I dug a little summary on them:
Maintainer: Dan Koch Source: https://github.com/dmkoch/django-jsonfield Current Version: 2.0.2 PyPI: https://pypi.python.org/pypi/jsonfield
Maintainer: Matthew Schinckel Source: https://bitbucket.org/schinckel/django-jsonfield Current version: 1.0.1 PyPI: https://pypi.python.org/pypi/django-jsonfield
Post office uses the first one, by Dan Koch. It perfectly fulfills their needs.
But it doesn’t work correctly with django.utils.model_to_dict
- it
returns dumped JSON string representation, instead of object.
Here is the code responsible for this [mis]behavior:
https://github.com/dmkoch/django-jsonfield/blob/master/jsonfield/fields.py#L110
def value_from_object(self, obj, dump=True):
value = super(JSONFieldBase, self).value_from_object(obj)
if self.null and value is None:
return None
return self.dumps_for_display(value) if dump else value
It dumps value by default, while model_to_dict expects it to return value unchanged.
JSONField by Matthew Schinckel doesn’t override value_from_object
and works as Django expects.
Once we discovered the full story we had three options:
Switch from
django-post-office
to another library that doesn’t have dependency conflicts.Extract email sending to separate service and remove dependency conflict.
Inherit Koch’s jsonfield and override problematic method.
I went with option 3.
At the same time I opened a PR to django-post-office
to switch to the one and true django-jsonfield
.