File: json.py

package info (click to toggle)
python-django-extensions 1.7.4-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 2,016 kB
  • ctags: 1,342
  • sloc: python: 8,873; makefile: 117
file content (114 lines) | stat: -rw-r--r-- 3,170 bytes parent folder | download
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# -*- coding: utf-8 -*-
"""
JSONField automatically serializes most Python terms to JSON data.
Creates a TEXT field with a default value of "{}".  See test_json.py for
more information.

 from django.db import models
 from django_extensions.db.fields import json

 class LOL(models.Model):
     extra = json.JSONField()
"""
from __future__ import absolute_import

import json
import six
import warnings

from django.conf import settings
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models


def dumps(value):
    return DjangoJSONEncoder().encode(value)


def loads(txt):
    value = json.loads(
        txt,
        encoding=settings.DEFAULT_CHARSET
    )
    return value


class JSONDict(dict):
    """
    Hack so repr() called by dumpdata will output JSON instead of
    Python formatted data.  This way fixtures will work!
    """
    def __repr__(self):
        return dumps(self)


class JSONList(list):
    """
    As above
    """
    def __repr__(self):
        return dumps(self)


class JSONField(models.TextField):
    """JSONField is a generic textfield that neatly serializes/unserializes
    JSON objects seamlessly.  Main thingy must be a dict object."""

    def __init__(self, *args, **kwargs):
        warnings.warn("Django 1.9 features a native JsonField, this JSONField will "
            "be removed somewhere after Django 1.8 becomes unsupported.",
            DeprecationWarning)
        kwargs['default'] = kwargs.get('default', dict)
        models.TextField.__init__(self, *args, **kwargs)

    def get_default(self):
        if self.has_default():
            default = self.default

            if callable(default):
                default = default()

            return self.to_python(default)
        return super(JSONField, self).get_default()

    def to_python(self, value):
        """Convert our string value to JSON after we load it from the DB"""
        if value is None or value == '':
            return {}

        if isinstance(value, six.string_types):
            res = loads(value)
        else:
            res = value

        if isinstance(res, dict):
            return JSONDict(**res)
        elif isinstance(res, list):
            return JSONList(res)

        return value

    def get_prep_value(self, value):
        if not isinstance(value, six.string_types):
            return dumps(value)
        return super(models.TextField, self).get_prep_value(value)

    def from_db_value(self, value, expression, connection, context):
        return self.to_python(value)

    def get_db_prep_save(self, value, connection, **kwargs):
        """Convert our JSON object to a string before we save"""
        if value is None and self.null:
            return None
        # default values come in as strings; only non-strings should be
        # run through `dumps`
        if not isinstance(value, six.string_types):
            value = dumps(value)

        return value

    def deconstruct(self):
        name, path, args, kwargs = super(JSONField, self).deconstruct()
        if self.default == '{}':
            del kwargs['default']
        return name, path, args, kwargs