Uploaded image for project: 'Python Integrations'
  1. Python Integrations
  2. INTPYTHON-551

Add a JSONField that can store BSON data types

    • Type: Icon: New Feature New Feature
    • Resolution: Unresolved
    • Priority: Icon: Minor - P4 Minor - P4
    • None
    • Affects Version/s: None
    • Component/s: django
    • None
    • Python Drivers
    • Hide

      1. What would you like to communicate to the user about this feature?
      2. Would you like the user to see examples of the syntax and/or executable code and its output?
      3. Which versions of the driver/connector does this apply to?

      Show
      1. What would you like to communicate to the user about this feature? 2. Would you like the user to see examples of the syntax and/or executable code and its output? 3. Which versions of the driver/connector does this apply to?
    • None
    • None
    • None
    • None
    • None
    • None

      As requested on the community forum, it would be useful to have JSONField support BSON data types. Unfortunately, this may require a custom field or a change to Django's built-in JSONField.

      Alternatively, I worked up a quick example using a custom decoder/encoder that appears to work to some extent:

      diff --git a/django_mongodb_backend/operations.py b/django_mongodb_backend/operations.py
      index cb1e93d..dcdf7fb 100644
      --- a/django_mongodb_backend/operations.py
      +++ b/django_mongodb_backend/operations.py
      @@ -147,7 +147,9 @@ class DatabaseOperations(BaseDatabaseOperations):
               Convert dict data to a string so that JSONField.from_db_value() can
               decode it using json.loads().
               """
      -        return json.dumps(value)
      +        target = getattr(expression, 'target', None)
      +        encoder = target.encoder if target else None
      +        return json.dumps(value, cls=encoder)
       
           def convert_timefield_value(self, value, expression, connection):
               if value is not None:
      diff --git a/tests/model_fields_/models.py b/tests/model_fields_/models.py
      index b25b94a..b8fa3c0 100644
      --- a/tests/model_fields_/models.py
      +++ b/tests/model_fields_/models.py
      @@ -6,6 +6,25 @@ from django_mongodb_backend.fields import ArrayField, EmbeddedModelField, Object
       from django_mongodb_backend.models import EmbeddedModel
       
       
      +import json
      +from bson import json_util
      +
      +
      +class BSONEncoder(json.JSONEncoder):
      +    def encode(self, obj):
      +        return json_util.dumps(obj)
      +
      +
      +class BSONDecoder(json.JSONDecoder):
      +    def decode(self, obj):
      +        return json_util.loads(obj)
      +
      +
      +# JSONField
      +class JSONModel(models.Model):
      +    value = models.JSONField(encoder=BSONEncoder, decoder=BSONDecoder)
      +
      +
       # ObjectIdField
       class ObjectIdModel(models.Model):
           field = ObjectIdField()
      diff --git a/tests/model_fields_/test_jsonfield.py b/tests/model_fields_/test_jsonfield.py
      new file mode 100644
      index 0000000..a860b7b
      --- /dev/null
      +++ b/tests/model_fields_/test_jsonfield.py
      @@ -0,0 +1,15 @@
      +from django.db.models import JSONField
      +from django.test import TestCase
      +from bson import ObjectId
      +
      +from .models import JSONModel
      +
      +
      +class TestSaveLoad(TestCase):
      +    def test_null(self):
      +        object_id = ObjectId()
      +        value = {"key": object_id}
      +        obj = JSONModel(value=value)
      +        obj.save()
      +        obj.refresh_from_db()
      +        self.assertEqual(obj.value, value)

      In order for querying to work as expected, the field would require custom Django lookups to prepare BSON types into the dictionary format the bson.json_util.dumps() outputs, e.g. {}{"$oid": "67d5f6f5188e40f99400bc15"} for ObjectId.

            Assignee:
            Unassigned Unassigned
            Reporter:
            tim.graham@mongodb.com Tim Graham
            Votes:
            1 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              None
              None
              None
              None