Login

JSONField

Author:
Jasber
Posted:
May 2, 2009
Language:
Python
Version:
1.0
Tags:
jsonfield json db field model json-field models fields
Score:
3 (after 3 ratings)

This is a custom field that lets you easily store JSON data in one of your model fields. This is updated to work with Django 1.1.

Example: (models.py)

from django.db import models
import JSONField

class MyModel(models.Model):
    info = JSONField()

Example: (shell)

>>> obj = MyModel.objects.all()[0]
>>> type(obj.info)
<type 'NoneType'>
>>> obj.info = {"test": [1, 2, 3]}
>>> obj.save()

Code at GitHub

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from django.db import models
from django.core.serializers.json import DjangoJSONEncoder
from django.utils import simplejson as json

class JSONField(models.TextField):
    """JSONField is a generic textfield that neatly serializes/unserializes
    JSON objects seamlessly"""

    # Used so to_python() is called
    __metaclass__ = models.SubfieldBase

    def to_python(self, value):
        """Convert our string value to JSON after we load it from the DB"""

        if value == "":
            return None

        try:
            if isinstance(value, basestring):
                return json.loads(value)
        except ValueError:
            pass

        return value

    def get_db_prep_save(self, value):
        """Convert our JSON object to a string before we save"""

        if value == "":
            return None

        if isinstance(value, dict):
            value = json.dumps(value, cls=DjangoJSONEncoder)

        return super(JSONField, self).get_db_prep_save(value)

More like this

  1. Custom model field to store dict object in database by rudyryk 6 years, 4 months ago
  2. JSONField by deadwisdom 9 years ago
  3. SerializedObjectField by dominno 6 years, 5 months ago
  4. CSV to JSON Fixture by briangershon 7 years ago
  5. A widget that renders JSON data as separate editable inputs. by abbasovalex 3 years, 1 month ago

Comments

cpesch (on May 6, 2009):
<p>I have problems loading json (with loaddata) that has been dumped (with dumpdata) from a PostgreSQL database. The to_python method is given a value like:</p> <pre>{u'source': u'web', u'location': u'home'} </pre> <p>and this leads to the error message:</p> <pre>ValueError: Expecting property name: line 1 column 1 (char 1) </pre> <p>loads() seems to expect something like</p> <pre>{"source": "web", "location": "home"} </pre> <p>Inserting something like</p> <pre>value = value.replace("u'", "'").replace("'",'"') </pre> <p>before line 19 cures the problem, but that doesn't look like an elegant solution, isn't it?</p>

#

jb0t (on May 13, 2009):
<p>This worked a little better for me. Though I am still not all that happy with it.</p> <pre># # # # # # # # # # # # # # # # # from django.db import models from django.core.serializers.json import DjangoJSONEncoder from django.utils import simplejson as json class JSONField(models.TextField): """JSONField is a generic textfield that neatly serializes/unserializes JSON objects seamlessly The json spec claims you must use a collection type at the top level of the data structure. However the simplesjon decoder and Firefox both encode and decode non collection types that do not exist inside a collection. The to_python method relies on the value being an instance of basestring to ensure that it is encoded. If a string is the sole value at the point the field is instanced, to_python attempts to decode the sting because it is derived from basestring but cannot be encodeded and throws the exception ValueError: No JSON object could be decoded. """ # Used so to_python() is called __metaclass__ = models.SubfieldBase def to_python(self, value): """Convert our string value to JSON after we load it from the DB""" if value == "": return None try: if isinstance(value, basestring): return json.loads(value) except ValueError: return value return value def get_db_prep_save(self, value): """Convert our JSON object to a string before we save""" if value == "": return None return super(JSONField, self).get_db_prep_save(json.dumps(value, cls=DjangoJSONEncoder)) </pre>

#

jb0t (on May 13, 2009):
<p>Last sentence should actually read</p> <p>If a string is the sole value at the point the field is instanced, to_python attempts to decode the sting because it is derived from basestring but cannot be decoded</p>

#

Jasber (on May 20, 2009):
<p>jb0t: I updated what you added, thanks.</p> <p>cpesch: I'm really not sure, I don't have PostgreSQL installed on my machine. Also no, that doesn't look like a very elegant solution. If you can come up with something let me know and I'll add it.</p>

#

fmardini (on June 18, 2009):
<p>a hopefully elegant solution :)</p> <p>value = dict(map(lambda n: map(str, n), value.items()))</p>

#

Jasber (on June 24, 2009):
<p>fmardini: I think that may cover most use cases, but simply mapping complex objects to str isn't going to work for everyone.</p>

#

chowdere (on August 11, 2009):
<p>I was being thrown for a loop with e.g. "Error binding parameter" (using sqlite) when trying to save a model object that had a JSONField. Turns out I was passing in a list, not a dict (valid JSON), and this JSONField doesn't handle lists. The quick fix has line 32 become:</p> <pre> if isinstance(value, dict) or isinstance(value, list): </pre>

#

andrewbadr (on November 9, 2009):
<p>I'm only using JSONField for holding dictionaries, which I suspect is pretty common. So, I took out the other options. Use like "properties = DictField(default={})".</p> <pre>class DictField(models.TextField): """DictField is a textfield that contains JSON-serialized dictionaries.""" # Used so to_python() is called __metaclass__ = models.SubfieldBase def to_python(self, value): """Convert our string value to JSON after we load it from the DB""" value = json.loads(value) assert isinstance(value, dict) return value def get_db_prep_save(self, value): """Convert our JSON object to a string before we save""" assert isinstance(value, dict) value = json.dumps(value, cls=DjangoJSONEncoder) return super(DictField, self).get_db_prep_save(value) </pre>

#

g4b (on July 7, 2010):
<p>modified it, so it can hold lists and does serialize (at least i tried json serialization) also modified after the docs to use get_prep_value, since they write its the function to use as antidote to to_python.</p> <p>class JSONField(models.TextField): """JSONField is a generic textfield that neatly serializes/unserializes JSON objects seamlessly"""</p> <pre># Used so to_python() is called __metaclass__ = models.SubfieldBase def to_python(self, value): """Convert our string value to JSON after we load it from the DB""" if value == "": return None try: if isinstance(value, basestring): return json.loads(value) except ValueError: pass return value def get_prep_value(self, value): """Convert our JSON object to a string before we save""" if value == "": return None if isinstance(value, dict) or isinstance(value, dict): value = json.dumps(value, cls=DjangoJSONEncoder) return super(JSONField, self).get_prep_value(value) def value_to_string(self, obj): """ called by the serializer. """ value = self._get_val_from_obj(obj) return self.get_db_prep_value(value) </pre>

#

hijinks (on May 11, 2012):
<p>Fix for 1.4. Need to add connection into the get_db_prep_save()</p> <pre>def get_db_prep_save(self, value, connection): """Convert our JSON object to a string before we save""" if value == "": return None if isinstance(value, dict): value = json.dumps(value, cls=DjangoJSONEncoder) return super(JSONField, self).get_db_prep_save(value, connection) </pre>

#

Please login first before commenting.