Import python-django-jsonfield_1.0.1.orig.tar.gz
[dgit import orig python-django-jsonfield_1.0.1.orig.tar.gz]
This commit is contained in:
commit
ae9fcb7da0
|
@ -0,0 +1,23 @@
|
||||||
|
Copyright (c) 2012, Matthew Schinckel.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* The names of its contributors may not be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL MATTHEW SCHINCKEL BE LIABLE FOR ANY DIRECT,
|
||||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||||
|
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,6 @@
|
||||||
|
recursive-include jsonfield *.py
|
||||||
|
include jsonfield/VERSION
|
||||||
|
include README.rst
|
||||||
|
include tests.py
|
||||||
|
include LICENSE
|
||||||
|
recursive-exclude jsonfield *.pyc
|
|
@ -0,0 +1,227 @@
|
||||||
|
Metadata-Version: 1.1
|
||||||
|
Name: django-jsonfield
|
||||||
|
Version: 1.0.1
|
||||||
|
Summary: JSONField for django models
|
||||||
|
Home-page: http://bitbucket.org/schinckel/django-jsonfield/
|
||||||
|
Author: Matthew Schinckel
|
||||||
|
Author-email: matt@schinckel.net
|
||||||
|
License: UNKNOWN
|
||||||
|
Description: django-jsonfield
|
||||||
|
===================
|
||||||
|
|
||||||
|
.. image:: https://codeship.com/projects/2e1a3d30-7db7-0132-629f-4abd151a3721/status?branch=default
|
||||||
|
|
||||||
|
I had a serious need for a JSON field for django. There were a couple out
|
||||||
|
there, but none packaged up nicely on bitbucket/github that were usable
|
||||||
|
with ``pip install -e``.
|
||||||
|
|
||||||
|
So I took the code from `David Cramer's blog`_, and packaged it up.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
To use, just install the package, and then use the field::
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
import jsonfield
|
||||||
|
|
||||||
|
class MyModel(models.Model):
|
||||||
|
the_json = jsonfield.JSONField()
|
||||||
|
|
||||||
|
You can assign any JSON-encodable object to this field. It will be
|
||||||
|
JSON-encoded before being stored in the database as a text value and it
|
||||||
|
will be turned back into a python list/dict/string upon retrieval from the
|
||||||
|
database.
|
||||||
|
|
||||||
|
There is also a ``TypedJSONField``, that allows you to define data types that must be included within each object in the array. More documentation to follow.
|
||||||
|
|
||||||
|
|
||||||
|
Notes
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
If no ``default`` is provided, and ``null=True`` is not passed in to the
|
||||||
|
field constructor, then a default of ``{}`` will be used.
|
||||||
|
|
||||||
|
|
||||||
|
Supported django versions
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
All versions of Django from 1.8 onwards are tested, however, if you are using Postgres, I highly recommend that you consider using the ``django.contrib.postgres`` module's ``JSONField`` instead.
|
||||||
|
|
||||||
|
Extras
|
||||||
|
------
|
||||||
|
|
||||||
|
jsonify templatetag
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
This allows you to convert a python data structure into JSON within a template::
|
||||||
|
|
||||||
|
{% load jsonify %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var foo = {{ bar|jsonify|safe }};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
Note that you must only use the "safe" filter when you use the jsonify
|
||||||
|
filter within a <script> tag (which is parsed like a CDATA section).
|
||||||
|
|
||||||
|
If you use it in some other places like in an HTML attribute, then
|
||||||
|
you must not use the safe filter so that its output is properly escaped::
|
||||||
|
|
||||||
|
<div data-foo="{{ bar|jsonify }}">
|
||||||
|
|
||||||
|
The above rules are important to avoid XSS attacks with unsafe strings
|
||||||
|
stored in the converted data structure.
|
||||||
|
|
||||||
|
History
|
||||||
|
----------
|
||||||
|
|
||||||
|
1.0.1
|
||||||
|
~~~~~~
|
||||||
|
Fix issue with Postgres JSONB fields.
|
||||||
|
Limit XSS attacks with jsonify template tag.
|
||||||
|
|
||||||
|
|
||||||
|
1.0.0
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
Add support for Django 1.8 and 1.9 (without warnings). Remove support for Django < 1.8
|
||||||
|
as none of those releases are supported upstream anyway.
|
||||||
|
|
||||||
|
With this version, ``JSONField`` no longer decodes assigned string values as JSON. Instead it assumes that any value that you assign is the decoded value which will be JSON-encoded before storage in the database. This explains the bump to version 1.0 as it's a backwards incompatible change.
|
||||||
|
|
||||||
|
0.9.19
|
||||||
|
~~~~~~
|
||||||
|
Allow passing `decoder_kwargs` as an argument to a field. This dict will be passed as kwargs to
|
||||||
|
the `json.loads()` calls when loading data that is a string.
|
||||||
|
|
||||||
|
You may also set this as a global value in settings.JSONFIELD_DECODER_KWARGS.
|
||||||
|
|
||||||
|
A new dict is created for each field: so if this value is altered after field definition, it shouldn't
|
||||||
|
affect already attached fields.
|
||||||
|
|
||||||
|
0.9.16
|
||||||
|
~~~~~~
|
||||||
|
Allow passing an argument of `encoder_class` to a field, which will result in that object (or
|
||||||
|
the object located at that path, for instance `core.utils.JSONEncoder`) being used as the `cls`
|
||||||
|
argument when serializing objects.
|
||||||
|
|
||||||
|
You may also set this as a global value in settings.JSONFIELD_ENCODER_CLASS
|
||||||
|
|
||||||
|
0.9.15
|
||||||
|
~~~~~~
|
||||||
|
Bump version number to get around uploading issues.
|
||||||
|
|
||||||
|
0.9.14
|
||||||
|
~~~~~~
|
||||||
|
No longer hit the db to work out db_type.
|
||||||
|
|
||||||
|
0.9.12
|
||||||
|
~~~~~~
|
||||||
|
Cache the result of db_type.
|
||||||
|
Handle incoming data from multiple select widget better.
|
||||||
|
|
||||||
|
0.9.9
|
||||||
|
~~~~~
|
||||||
|
Finally strip out non-required files.
|
||||||
|
|
||||||
|
0.9.8
|
||||||
|
~~~~~
|
||||||
|
Remove freezegun workarounds.
|
||||||
|
Fix broken build.
|
||||||
|
|
||||||
|
0.9.4
|
||||||
|
~~~~~
|
||||||
|
Fixes for mutable defaults: we serialize and then deserialize in this
|
||||||
|
case, so you can still use ``default={}``.
|
||||||
|
|
||||||
|
0.9.3
|
||||||
|
~~~~~
|
||||||
|
Remove support for storing data using Postgres' 9.2's JSON data type, as
|
||||||
|
you cannot currently query against this!
|
||||||
|
|
||||||
|
Remove support for django < 1.3.
|
||||||
|
|
||||||
|
|
||||||
|
0.9.0
|
||||||
|
~~~~~
|
||||||
|
Add LICENSE file.
|
||||||
|
Added TypedJSONField.
|
||||||
|
|
||||||
|
|
||||||
|
0.8.10
|
||||||
|
~~~~~~
|
||||||
|
Allow ``{{ variable|jsonify }}`` to work with querysets.
|
||||||
|
|
||||||
|
0.8.8
|
||||||
|
~~~~~
|
||||||
|
Prevent circular import problem with django 1.3.1 and gargoyle.
|
||||||
|
|
||||||
|
0.8.7
|
||||||
|
~~~~~
|
||||||
|
Better handle null=True and blank=True: it should make sense what they do now.
|
||||||
|
|
||||||
|
0.8.5
|
||||||
|
~~~~~
|
||||||
|
Allow for '{}' and '[]', and make them not appear to be None.
|
||||||
|
|
||||||
|
0.8.4
|
||||||
|
~~~~~
|
||||||
|
Ensure the version number file is installed with the package.
|
||||||
|
|
||||||
|
0.8.3
|
||||||
|
~~~~~
|
||||||
|
Store the version number in one place only, now.
|
||||||
|
|
||||||
|
0.8.2
|
||||||
|
~~~~~
|
||||||
|
Oops. Packaging error prevented install from pypi. Added README.rst to manifest.
|
||||||
|
|
||||||
|
0.8.1
|
||||||
|
~~~~~
|
||||||
|
Converting to string does nothing, as serializing a model instance with a JSONField would have a string version of that field, instead of it embedded inline. (Back to pre 0.8 behaviour).
|
||||||
|
|
||||||
|
Added better querying support: (``field__contains={'key':'value','key2':'value2'}`` works.)
|
||||||
|
|
||||||
|
Removed JSONTableWidget from package.
|
||||||
|
|
||||||
|
0.8
|
||||||
|
~~~
|
||||||
|
(Many thanks to `IanLewis`_ for these features)
|
||||||
|
|
||||||
|
Supports django 1.2
|
||||||
|
|
||||||
|
Supports callable and json serializable objects as default
|
||||||
|
|
||||||
|
Implemented get_db_prep_value()
|
||||||
|
|
||||||
|
Add tests and test runner.
|
||||||
|
|
||||||
|
Removed JSONTableWidget from README.
|
||||||
|
|
||||||
|
0.7.1
|
||||||
|
~~~~~
|
||||||
|
Don't fail when trying to install before django is installed.
|
||||||
|
|
||||||
|
0.7
|
||||||
|
~~~
|
||||||
|
First time I tagged releases.
|
||||||
|
|
||||||
|
|
||||||
|
Todo
|
||||||
|
----------
|
||||||
|
Allow for passing in a function to use for processing unknown data types.
|
||||||
|
|
||||||
|
Convert date/time objects nicely to/from ISO strings (YYYY-mm-dd HH:MM:SS
|
||||||
|
TZNAME). This is actually a bit tricky, as we don't know if we are expecting
|
||||||
|
a date/time object. We may parse objects as we go, but there could be
|
||||||
|
some performance issues with this. I'm tempted to say "only do this on TypedJSONField()"
|
||||||
|
|
||||||
|
.. _David Cramer's blog: http://justcramer.com/2009/04/14/cleaning-up-with-json-and-sql/
|
||||||
|
.. _IanLewis: https://bitbucket.org/IanLewis
|
||||||
|
|
||||||
|
Platform: UNKNOWN
|
||||||
|
Classifier: Programming Language :: Python
|
||||||
|
Classifier: Programming Language :: Python :: 3
|
||||||
|
Classifier: License :: OSI Approved :: BSD License
|
||||||
|
Classifier: Operating System :: OS Independent
|
||||||
|
Classifier: Framework :: Django
|
|
@ -0,0 +1,212 @@
|
||||||
|
django-jsonfield
|
||||||
|
===================
|
||||||
|
|
||||||
|
.. image:: https://codeship.com/projects/2e1a3d30-7db7-0132-629f-4abd151a3721/status?branch=default
|
||||||
|
|
||||||
|
I had a serious need for a JSON field for django. There were a couple out
|
||||||
|
there, but none packaged up nicely on bitbucket/github that were usable
|
||||||
|
with ``pip install -e``.
|
||||||
|
|
||||||
|
So I took the code from `David Cramer's blog`_, and packaged it up.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
To use, just install the package, and then use the field::
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
import jsonfield
|
||||||
|
|
||||||
|
class MyModel(models.Model):
|
||||||
|
the_json = jsonfield.JSONField()
|
||||||
|
|
||||||
|
You can assign any JSON-encodable object to this field. It will be
|
||||||
|
JSON-encoded before being stored in the database as a text value and it
|
||||||
|
will be turned back into a python list/dict/string upon retrieval from the
|
||||||
|
database.
|
||||||
|
|
||||||
|
There is also a ``TypedJSONField``, that allows you to define data types that must be included within each object in the array. More documentation to follow.
|
||||||
|
|
||||||
|
|
||||||
|
Notes
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
If no ``default`` is provided, and ``null=True`` is not passed in to the
|
||||||
|
field constructor, then a default of ``{}`` will be used.
|
||||||
|
|
||||||
|
|
||||||
|
Supported django versions
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
All versions of Django from 1.8 onwards are tested, however, if you are using Postgres, I highly recommend that you consider using the ``django.contrib.postgres`` module's ``JSONField`` instead.
|
||||||
|
|
||||||
|
Extras
|
||||||
|
------
|
||||||
|
|
||||||
|
jsonify templatetag
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
This allows you to convert a python data structure into JSON within a template::
|
||||||
|
|
||||||
|
{% load jsonify %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var foo = {{ bar|jsonify|safe }};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
Note that you must only use the "safe" filter when you use the jsonify
|
||||||
|
filter within a <script> tag (which is parsed like a CDATA section).
|
||||||
|
|
||||||
|
If you use it in some other places like in an HTML attribute, then
|
||||||
|
you must not use the safe filter so that its output is properly escaped::
|
||||||
|
|
||||||
|
<div data-foo="{{ bar|jsonify }}">
|
||||||
|
|
||||||
|
The above rules are important to avoid XSS attacks with unsafe strings
|
||||||
|
stored in the converted data structure.
|
||||||
|
|
||||||
|
History
|
||||||
|
----------
|
||||||
|
|
||||||
|
1.0.1
|
||||||
|
~~~~~~
|
||||||
|
Fix issue with Postgres JSONB fields.
|
||||||
|
Limit XSS attacks with jsonify template tag.
|
||||||
|
|
||||||
|
|
||||||
|
1.0.0
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
Add support for Django 1.8 and 1.9 (without warnings). Remove support for Django < 1.8
|
||||||
|
as none of those releases are supported upstream anyway.
|
||||||
|
|
||||||
|
With this version, ``JSONField`` no longer decodes assigned string values as JSON. Instead it assumes that any value that you assign is the decoded value which will be JSON-encoded before storage in the database. This explains the bump to version 1.0 as it's a backwards incompatible change.
|
||||||
|
|
||||||
|
0.9.19
|
||||||
|
~~~~~~
|
||||||
|
Allow passing `decoder_kwargs` as an argument to a field. This dict will be passed as kwargs to
|
||||||
|
the `json.loads()` calls when loading data that is a string.
|
||||||
|
|
||||||
|
You may also set this as a global value in settings.JSONFIELD_DECODER_KWARGS.
|
||||||
|
|
||||||
|
A new dict is created for each field: so if this value is altered after field definition, it shouldn't
|
||||||
|
affect already attached fields.
|
||||||
|
|
||||||
|
0.9.16
|
||||||
|
~~~~~~
|
||||||
|
Allow passing an argument of `encoder_class` to a field, which will result in that object (or
|
||||||
|
the object located at that path, for instance `core.utils.JSONEncoder`) being used as the `cls`
|
||||||
|
argument when serializing objects.
|
||||||
|
|
||||||
|
You may also set this as a global value in settings.JSONFIELD_ENCODER_CLASS
|
||||||
|
|
||||||
|
0.9.15
|
||||||
|
~~~~~~
|
||||||
|
Bump version number to get around uploading issues.
|
||||||
|
|
||||||
|
0.9.14
|
||||||
|
~~~~~~
|
||||||
|
No longer hit the db to work out db_type.
|
||||||
|
|
||||||
|
0.9.12
|
||||||
|
~~~~~~
|
||||||
|
Cache the result of db_type.
|
||||||
|
Handle incoming data from multiple select widget better.
|
||||||
|
|
||||||
|
0.9.9
|
||||||
|
~~~~~
|
||||||
|
Finally strip out non-required files.
|
||||||
|
|
||||||
|
0.9.8
|
||||||
|
~~~~~
|
||||||
|
Remove freezegun workarounds.
|
||||||
|
Fix broken build.
|
||||||
|
|
||||||
|
0.9.4
|
||||||
|
~~~~~
|
||||||
|
Fixes for mutable defaults: we serialize and then deserialize in this
|
||||||
|
case, so you can still use ``default={}``.
|
||||||
|
|
||||||
|
0.9.3
|
||||||
|
~~~~~
|
||||||
|
Remove support for storing data using Postgres' 9.2's JSON data type, as
|
||||||
|
you cannot currently query against this!
|
||||||
|
|
||||||
|
Remove support for django < 1.3.
|
||||||
|
|
||||||
|
|
||||||
|
0.9.0
|
||||||
|
~~~~~
|
||||||
|
Add LICENSE file.
|
||||||
|
Added TypedJSONField.
|
||||||
|
|
||||||
|
|
||||||
|
0.8.10
|
||||||
|
~~~~~~
|
||||||
|
Allow ``{{ variable|jsonify }}`` to work with querysets.
|
||||||
|
|
||||||
|
0.8.8
|
||||||
|
~~~~~
|
||||||
|
Prevent circular import problem with django 1.3.1 and gargoyle.
|
||||||
|
|
||||||
|
0.8.7
|
||||||
|
~~~~~
|
||||||
|
Better handle null=True and blank=True: it should make sense what they do now.
|
||||||
|
|
||||||
|
0.8.5
|
||||||
|
~~~~~
|
||||||
|
Allow for '{}' and '[]', and make them not appear to be None.
|
||||||
|
|
||||||
|
0.8.4
|
||||||
|
~~~~~
|
||||||
|
Ensure the version number file is installed with the package.
|
||||||
|
|
||||||
|
0.8.3
|
||||||
|
~~~~~
|
||||||
|
Store the version number in one place only, now.
|
||||||
|
|
||||||
|
0.8.2
|
||||||
|
~~~~~
|
||||||
|
Oops. Packaging error prevented install from pypi. Added README.rst to manifest.
|
||||||
|
|
||||||
|
0.8.1
|
||||||
|
~~~~~
|
||||||
|
Converting to string does nothing, as serializing a model instance with a JSONField would have a string version of that field, instead of it embedded inline. (Back to pre 0.8 behaviour).
|
||||||
|
|
||||||
|
Added better querying support: (``field__contains={'key':'value','key2':'value2'}`` works.)
|
||||||
|
|
||||||
|
Removed JSONTableWidget from package.
|
||||||
|
|
||||||
|
0.8
|
||||||
|
~~~
|
||||||
|
(Many thanks to `IanLewis`_ for these features)
|
||||||
|
|
||||||
|
Supports django 1.2
|
||||||
|
|
||||||
|
Supports callable and json serializable objects as default
|
||||||
|
|
||||||
|
Implemented get_db_prep_value()
|
||||||
|
|
||||||
|
Add tests and test runner.
|
||||||
|
|
||||||
|
Removed JSONTableWidget from README.
|
||||||
|
|
||||||
|
0.7.1
|
||||||
|
~~~~~
|
||||||
|
Don't fail when trying to install before django is installed.
|
||||||
|
|
||||||
|
0.7
|
||||||
|
~~~
|
||||||
|
First time I tagged releases.
|
||||||
|
|
||||||
|
|
||||||
|
Todo
|
||||||
|
----------
|
||||||
|
Allow for passing in a function to use for processing unknown data types.
|
||||||
|
|
||||||
|
Convert date/time objects nicely to/from ISO strings (YYYY-mm-dd HH:MM:SS
|
||||||
|
TZNAME). This is actually a bit tricky, as we don't know if we are expecting
|
||||||
|
a date/time object. We may parse objects as we go, but there could be
|
||||||
|
some performance issues with this. I'm tempted to say "only do this on TypedJSONField()"
|
||||||
|
|
||||||
|
.. _David Cramer's blog: http://justcramer.com/2009/04/14/cleaning-up-with-json-and-sql/
|
||||||
|
.. _IanLewis: https://bitbucket.org/IanLewis
|
|
@ -0,0 +1,227 @@
|
||||||
|
Metadata-Version: 1.1
|
||||||
|
Name: django-jsonfield
|
||||||
|
Version: 1.0.1
|
||||||
|
Summary: JSONField for django models
|
||||||
|
Home-page: http://bitbucket.org/schinckel/django-jsonfield/
|
||||||
|
Author: Matthew Schinckel
|
||||||
|
Author-email: matt@schinckel.net
|
||||||
|
License: UNKNOWN
|
||||||
|
Description: django-jsonfield
|
||||||
|
===================
|
||||||
|
|
||||||
|
.. image:: https://codeship.com/projects/2e1a3d30-7db7-0132-629f-4abd151a3721/status?branch=default
|
||||||
|
|
||||||
|
I had a serious need for a JSON field for django. There were a couple out
|
||||||
|
there, but none packaged up nicely on bitbucket/github that were usable
|
||||||
|
with ``pip install -e``.
|
||||||
|
|
||||||
|
So I took the code from `David Cramer's blog`_, and packaged it up.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
To use, just install the package, and then use the field::
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
import jsonfield
|
||||||
|
|
||||||
|
class MyModel(models.Model):
|
||||||
|
the_json = jsonfield.JSONField()
|
||||||
|
|
||||||
|
You can assign any JSON-encodable object to this field. It will be
|
||||||
|
JSON-encoded before being stored in the database as a text value and it
|
||||||
|
will be turned back into a python list/dict/string upon retrieval from the
|
||||||
|
database.
|
||||||
|
|
||||||
|
There is also a ``TypedJSONField``, that allows you to define data types that must be included within each object in the array. More documentation to follow.
|
||||||
|
|
||||||
|
|
||||||
|
Notes
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
If no ``default`` is provided, and ``null=True`` is not passed in to the
|
||||||
|
field constructor, then a default of ``{}`` will be used.
|
||||||
|
|
||||||
|
|
||||||
|
Supported django versions
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
All versions of Django from 1.8 onwards are tested, however, if you are using Postgres, I highly recommend that you consider using the ``django.contrib.postgres`` module's ``JSONField`` instead.
|
||||||
|
|
||||||
|
Extras
|
||||||
|
------
|
||||||
|
|
||||||
|
jsonify templatetag
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
This allows you to convert a python data structure into JSON within a template::
|
||||||
|
|
||||||
|
{% load jsonify %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var foo = {{ bar|jsonify|safe }};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
Note that you must only use the "safe" filter when you use the jsonify
|
||||||
|
filter within a <script> tag (which is parsed like a CDATA section).
|
||||||
|
|
||||||
|
If you use it in some other places like in an HTML attribute, then
|
||||||
|
you must not use the safe filter so that its output is properly escaped::
|
||||||
|
|
||||||
|
<div data-foo="{{ bar|jsonify }}">
|
||||||
|
|
||||||
|
The above rules are important to avoid XSS attacks with unsafe strings
|
||||||
|
stored in the converted data structure.
|
||||||
|
|
||||||
|
History
|
||||||
|
----------
|
||||||
|
|
||||||
|
1.0.1
|
||||||
|
~~~~~~
|
||||||
|
Fix issue with Postgres JSONB fields.
|
||||||
|
Limit XSS attacks with jsonify template tag.
|
||||||
|
|
||||||
|
|
||||||
|
1.0.0
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
Add support for Django 1.8 and 1.9 (without warnings). Remove support for Django < 1.8
|
||||||
|
as none of those releases are supported upstream anyway.
|
||||||
|
|
||||||
|
With this version, ``JSONField`` no longer decodes assigned string values as JSON. Instead it assumes that any value that you assign is the decoded value which will be JSON-encoded before storage in the database. This explains the bump to version 1.0 as it's a backwards incompatible change.
|
||||||
|
|
||||||
|
0.9.19
|
||||||
|
~~~~~~
|
||||||
|
Allow passing `decoder_kwargs` as an argument to a field. This dict will be passed as kwargs to
|
||||||
|
the `json.loads()` calls when loading data that is a string.
|
||||||
|
|
||||||
|
You may also set this as a global value in settings.JSONFIELD_DECODER_KWARGS.
|
||||||
|
|
||||||
|
A new dict is created for each field: so if this value is altered after field definition, it shouldn't
|
||||||
|
affect already attached fields.
|
||||||
|
|
||||||
|
0.9.16
|
||||||
|
~~~~~~
|
||||||
|
Allow passing an argument of `encoder_class` to a field, which will result in that object (or
|
||||||
|
the object located at that path, for instance `core.utils.JSONEncoder`) being used as the `cls`
|
||||||
|
argument when serializing objects.
|
||||||
|
|
||||||
|
You may also set this as a global value in settings.JSONFIELD_ENCODER_CLASS
|
||||||
|
|
||||||
|
0.9.15
|
||||||
|
~~~~~~
|
||||||
|
Bump version number to get around uploading issues.
|
||||||
|
|
||||||
|
0.9.14
|
||||||
|
~~~~~~
|
||||||
|
No longer hit the db to work out db_type.
|
||||||
|
|
||||||
|
0.9.12
|
||||||
|
~~~~~~
|
||||||
|
Cache the result of db_type.
|
||||||
|
Handle incoming data from multiple select widget better.
|
||||||
|
|
||||||
|
0.9.9
|
||||||
|
~~~~~
|
||||||
|
Finally strip out non-required files.
|
||||||
|
|
||||||
|
0.9.8
|
||||||
|
~~~~~
|
||||||
|
Remove freezegun workarounds.
|
||||||
|
Fix broken build.
|
||||||
|
|
||||||
|
0.9.4
|
||||||
|
~~~~~
|
||||||
|
Fixes for mutable defaults: we serialize and then deserialize in this
|
||||||
|
case, so you can still use ``default={}``.
|
||||||
|
|
||||||
|
0.9.3
|
||||||
|
~~~~~
|
||||||
|
Remove support for storing data using Postgres' 9.2's JSON data type, as
|
||||||
|
you cannot currently query against this!
|
||||||
|
|
||||||
|
Remove support for django < 1.3.
|
||||||
|
|
||||||
|
|
||||||
|
0.9.0
|
||||||
|
~~~~~
|
||||||
|
Add LICENSE file.
|
||||||
|
Added TypedJSONField.
|
||||||
|
|
||||||
|
|
||||||
|
0.8.10
|
||||||
|
~~~~~~
|
||||||
|
Allow ``{{ variable|jsonify }}`` to work with querysets.
|
||||||
|
|
||||||
|
0.8.8
|
||||||
|
~~~~~
|
||||||
|
Prevent circular import problem with django 1.3.1 and gargoyle.
|
||||||
|
|
||||||
|
0.8.7
|
||||||
|
~~~~~
|
||||||
|
Better handle null=True and blank=True: it should make sense what they do now.
|
||||||
|
|
||||||
|
0.8.5
|
||||||
|
~~~~~
|
||||||
|
Allow for '{}' and '[]', and make them not appear to be None.
|
||||||
|
|
||||||
|
0.8.4
|
||||||
|
~~~~~
|
||||||
|
Ensure the version number file is installed with the package.
|
||||||
|
|
||||||
|
0.8.3
|
||||||
|
~~~~~
|
||||||
|
Store the version number in one place only, now.
|
||||||
|
|
||||||
|
0.8.2
|
||||||
|
~~~~~
|
||||||
|
Oops. Packaging error prevented install from pypi. Added README.rst to manifest.
|
||||||
|
|
||||||
|
0.8.1
|
||||||
|
~~~~~
|
||||||
|
Converting to string does nothing, as serializing a model instance with a JSONField would have a string version of that field, instead of it embedded inline. (Back to pre 0.8 behaviour).
|
||||||
|
|
||||||
|
Added better querying support: (``field__contains={'key':'value','key2':'value2'}`` works.)
|
||||||
|
|
||||||
|
Removed JSONTableWidget from package.
|
||||||
|
|
||||||
|
0.8
|
||||||
|
~~~
|
||||||
|
(Many thanks to `IanLewis`_ for these features)
|
||||||
|
|
||||||
|
Supports django 1.2
|
||||||
|
|
||||||
|
Supports callable and json serializable objects as default
|
||||||
|
|
||||||
|
Implemented get_db_prep_value()
|
||||||
|
|
||||||
|
Add tests and test runner.
|
||||||
|
|
||||||
|
Removed JSONTableWidget from README.
|
||||||
|
|
||||||
|
0.7.1
|
||||||
|
~~~~~
|
||||||
|
Don't fail when trying to install before django is installed.
|
||||||
|
|
||||||
|
0.7
|
||||||
|
~~~
|
||||||
|
First time I tagged releases.
|
||||||
|
|
||||||
|
|
||||||
|
Todo
|
||||||
|
----------
|
||||||
|
Allow for passing in a function to use for processing unknown data types.
|
||||||
|
|
||||||
|
Convert date/time objects nicely to/from ISO strings (YYYY-mm-dd HH:MM:SS
|
||||||
|
TZNAME). This is actually a bit tricky, as we don't know if we are expecting
|
||||||
|
a date/time object. We may parse objects as we go, but there could be
|
||||||
|
some performance issues with this. I'm tempted to say "only do this on TypedJSONField()"
|
||||||
|
|
||||||
|
.. _David Cramer's blog: http://justcramer.com/2009/04/14/cleaning-up-with-json-and-sql/
|
||||||
|
.. _IanLewis: https://bitbucket.org/IanLewis
|
||||||
|
|
||||||
|
Platform: UNKNOWN
|
||||||
|
Classifier: Programming Language :: Python
|
||||||
|
Classifier: Programming Language :: Python :: 3
|
||||||
|
Classifier: License :: OSI Approved :: BSD License
|
||||||
|
Classifier: Operating System :: OS Independent
|
||||||
|
Classifier: Framework :: Django
|
|
@ -0,0 +1,24 @@
|
||||||
|
LICENSE
|
||||||
|
MANIFEST.in
|
||||||
|
README.rst
|
||||||
|
setup.py
|
||||||
|
tests.py
|
||||||
|
django_jsonfield.egg-info/PKG-INFO
|
||||||
|
django_jsonfield.egg-info/SOURCES.txt
|
||||||
|
django_jsonfield.egg-info/dependency_links.txt
|
||||||
|
django_jsonfield.egg-info/top_level.txt
|
||||||
|
jsonfield/VERSION
|
||||||
|
jsonfield/__init__.py
|
||||||
|
jsonfield/fields.py
|
||||||
|
jsonfield/forms.py
|
||||||
|
jsonfield/models.py
|
||||||
|
jsonfield/utils.py
|
||||||
|
jsonfield/widgets.py
|
||||||
|
jsonfield/templatetags/__init__.py
|
||||||
|
jsonfield/templatetags/jsonify.py
|
||||||
|
jsonfield/tests/__init__.py
|
||||||
|
jsonfield/tests/test_fields.py
|
||||||
|
jsonfield/tests/test_forms.py
|
||||||
|
jsonfield/tests/jsonfield_test_app/__init__.py
|
||||||
|
jsonfield/tests/jsonfield_test_app/forms.py
|
||||||
|
jsonfield/tests/jsonfield_test_app/models.py
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
jsonfield
|
|
@ -0,0 +1 @@
|
||||||
|
1.0.1
|
|
@ -0,0 +1,8 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from .fields import JSONField
|
||||||
|
|
||||||
|
__all__ = ('JSONField',)
|
||||||
|
|
||||||
|
with open(os.path.join(os.path.dirname(__file__), 'VERSION')) as fp:
|
||||||
|
__version__ = fp.read().strip()
|
|
@ -0,0 +1,169 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import json
|
||||||
|
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import models
|
||||||
|
from django.db.backends.signals import connection_created
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.utils import six
|
||||||
|
|
||||||
|
from .utils import _resolve_object_path
|
||||||
|
from .widgets import JSONWidget
|
||||||
|
from .forms import JSONFormField
|
||||||
|
|
||||||
|
|
||||||
|
class JSONField(models.Field):
|
||||||
|
"""
|
||||||
|
A field that will ensure the data entered into it is valid JSON.
|
||||||
|
"""
|
||||||
|
default_error_messages = {
|
||||||
|
'invalid': _("'%s' is not a valid JSON string.")
|
||||||
|
}
|
||||||
|
description = "JSON object"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
if not kwargs.get('null', False):
|
||||||
|
kwargs['default'] = kwargs.get('default', dict)
|
||||||
|
self.encoder_kwargs = {
|
||||||
|
'indent': kwargs.pop('indent', getattr(settings, 'JSONFIELD_INDENT', None)),
|
||||||
|
}
|
||||||
|
# This can be an object (probably a class), or a path which can be imported, resulting
|
||||||
|
# in an object.
|
||||||
|
encoder_class = kwargs.pop('encoder_class', getattr(settings, 'JSONFIELD_ENCODER_CLASS', None))
|
||||||
|
if encoder_class:
|
||||||
|
self.encoder_kwargs['cls'] = _resolve_object_path(encoder_class)
|
||||||
|
|
||||||
|
self.decoder_kwargs = dict(kwargs.pop('decoder_kwargs', getattr(settings, 'JSONFIELD_DECODER_KWARGS', {})))
|
||||||
|
super(JSONField, self).__init__(*args, **kwargs)
|
||||||
|
self.validate(self.get_default(), None)
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {
|
||||||
|
'form_class': JSONFormField,
|
||||||
|
'widget': JSONWidget
|
||||||
|
}
|
||||||
|
defaults.update(**kwargs)
|
||||||
|
return super(JSONField, self).formfield(**defaults)
|
||||||
|
|
||||||
|
def validate(self, value, model_instance):
|
||||||
|
if not self.null and value is None:
|
||||||
|
raise ValidationError(self.error_messages['null'])
|
||||||
|
try:
|
||||||
|
self.get_prep_value(value)
|
||||||
|
except ValueError:
|
||||||
|
raise ValidationError(self.error_messages['invalid'] % value)
|
||||||
|
|
||||||
|
def get_default(self):
|
||||||
|
if self.has_default():
|
||||||
|
default = self.default
|
||||||
|
if callable(default):
|
||||||
|
default = default()
|
||||||
|
if isinstance(default, six.string_types):
|
||||||
|
return json.loads(default, **self.decoder_kwargs)
|
||||||
|
return json.loads(json.dumps(default, **self.encoder_kwargs), **self.decoder_kwargs)
|
||||||
|
return super(JSONField, self).get_default()
|
||||||
|
|
||||||
|
def get_internal_type(self):
|
||||||
|
return 'TextField'
|
||||||
|
|
||||||
|
def db_type(self, connection):
|
||||||
|
if connection.vendor == 'postgresql':
|
||||||
|
# Only do jsonb if in pg 9.4+
|
||||||
|
if connection.pg_version >= 90400:
|
||||||
|
return 'jsonb'
|
||||||
|
return 'text'
|
||||||
|
if connection.vendor == 'mysql':
|
||||||
|
return 'longtext'
|
||||||
|
if connection.vendor == 'oracle':
|
||||||
|
return 'long'
|
||||||
|
return 'text'
|
||||||
|
|
||||||
|
def from_db_value(self, value, expression, connection, context):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
return json.loads(value, **self.decoder_kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def get_db_prep_value(self, value, connection=None, prepared=None):
|
||||||
|
return self.get_prep_value(value)
|
||||||
|
|
||||||
|
def get_prep_value(self, value):
|
||||||
|
if value is None:
|
||||||
|
if not self.null and self.blank:
|
||||||
|
return ""
|
||||||
|
return None
|
||||||
|
return json.dumps(value, **self.encoder_kwargs)
|
||||||
|
|
||||||
|
def get_prep_lookup(self, lookup_type, value):
|
||||||
|
if lookup_type in ["exact", "iexact", "in", "isnull"]:
|
||||||
|
return value
|
||||||
|
if lookup_type in ["contains", "icontains"]:
|
||||||
|
if isinstance(value, (list, tuple)):
|
||||||
|
raise TypeError("Lookup type %r not supported with argument of %s" % (
|
||||||
|
lookup_type, type(value).__name__
|
||||||
|
))
|
||||||
|
# Need a way co combine the values with '%', but don't escape that.
|
||||||
|
return self.get_prep_value(value)[1:-1].replace(', ', r'%')
|
||||||
|
if isinstance(value, dict):
|
||||||
|
return self.get_prep_value(value)[1:-1]
|
||||||
|
return self.get_prep_value(value)
|
||||||
|
raise TypeError('Lookup type %r not supported' % lookup_type)
|
||||||
|
|
||||||
|
def value_to_string(self, obj):
|
||||||
|
return self._get_val_from_obj(obj)
|
||||||
|
|
||||||
|
|
||||||
|
class TypedJSONField(JSONField):
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.json_required_fields = kwargs.pop('required_fields', {})
|
||||||
|
self.json_validators = kwargs.pop('validators', [])
|
||||||
|
|
||||||
|
super(TypedJSONField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def cast_required_fields(self, obj):
|
||||||
|
if not obj:
|
||||||
|
return
|
||||||
|
for field_name, field_type in self.json_required_fields.items():
|
||||||
|
obj[field_name] = field_type.to_python(obj[field_name])
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
value = super(TypedJSONField, self).to_python(value)
|
||||||
|
|
||||||
|
if isinstance(value, list):
|
||||||
|
for item in value:
|
||||||
|
self.cast_required_fields(item)
|
||||||
|
else:
|
||||||
|
self.cast_required_fields(value)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def validate(self, value, model_instance):
|
||||||
|
super(TypedJSONField, self).validate(value, model_instance)
|
||||||
|
|
||||||
|
for v in self.json_validators:
|
||||||
|
if isinstance(value, list):
|
||||||
|
for item in value:
|
||||||
|
v(item)
|
||||||
|
else:
|
||||||
|
v(value)
|
||||||
|
|
||||||
|
|
||||||
|
def configure_database_connection(connection, **kwargs):
|
||||||
|
if connection.vendor != 'postgresql':
|
||||||
|
return
|
||||||
|
|
||||||
|
# Ensure that psycopg does not do JSON decoding under the hood
|
||||||
|
# We want to be able to do our own decoding with our own options
|
||||||
|
import psycopg2.extras
|
||||||
|
if hasattr(psycopg2.extras, 'register_default_jsonb'):
|
||||||
|
psycopg2.extras.register_default_jsonb(
|
||||||
|
connection.connection,
|
||||||
|
globally=False,
|
||||||
|
loads=lambda x: x)
|
||||||
|
|
||||||
|
|
||||||
|
connection_created.connect(configure_database_connection)
|
|
@ -0,0 +1,31 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.utils import six
|
||||||
|
|
||||||
|
from .widgets import JSONWidget
|
||||||
|
|
||||||
|
|
||||||
|
class JSONFormField(forms.CharField):
|
||||||
|
empty_values = [None, '']
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
if 'widget' not in kwargs:
|
||||||
|
kwargs['widget'] = JSONWidget
|
||||||
|
super(JSONFormField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
if isinstance(value, six.string_types) and value:
|
||||||
|
try:
|
||||||
|
return json.loads(value)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise forms.ValidationError(
|
||||||
|
'JSON decode error: %s' % (six.u(exc.args[0]),)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
|
def validate(self, value):
|
||||||
|
# This is required in older django versions.
|
||||||
|
if value in self.empty_values and self.required:
|
||||||
|
raise forms.ValidationError(self.error_messages['required'], code='required')
|
|
@ -0,0 +1,28 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
from jsonfield.utils import TZAwareJSONEncoder
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def jsonify(value):
|
||||||
|
# If we have a queryset, then convert it into a list.
|
||||||
|
if getattr(value, 'all', False):
|
||||||
|
value = list(value)
|
||||||
|
|
||||||
|
json_str = json.dumps(value, cls=TZAwareJSONEncoder)
|
||||||
|
|
||||||
|
unsafe_chars = {
|
||||||
|
'&': '\\u0026',
|
||||||
|
'<': '\\u003c',
|
||||||
|
'>': '\\u003e',
|
||||||
|
'\u2028': '\\u2028',
|
||||||
|
'\u2029': '\\u2029',
|
||||||
|
}
|
||||||
|
for (unsafe, safe) in unsafe_chars.items():
|
||||||
|
json_str = json_str.replace(unsafe, safe)
|
||||||
|
|
||||||
|
return json_str
|
|
@ -0,0 +1,2 @@
|
||||||
|
from .test_fields import * # NOQA
|
||||||
|
from .test_forms import * # NOQA
|
|
@ -0,0 +1,16 @@
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
from jsonfield.forms import JSONFormField
|
||||||
|
from .models import JSONFieldTestModel
|
||||||
|
|
||||||
|
|
||||||
|
class JSONTestForm(forms.Form):
|
||||||
|
json_data = JSONFormField()
|
||||||
|
optional_json_data = JSONFormField(required=False)
|
||||||
|
|
||||||
|
|
||||||
|
class JSONTestModelForm(forms.ModelForm):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = JSONFieldTestModel
|
||||||
|
exclude = []
|
|
@ -0,0 +1,31 @@
|
||||||
|
from django.db import models
|
||||||
|
from jsonfield.fields import JSONField
|
||||||
|
|
||||||
|
|
||||||
|
class JSONFieldTestModel(models.Model):
|
||||||
|
json = JSONField("test", null=True, blank=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
app_label = 'jsonfield'
|
||||||
|
|
||||||
|
|
||||||
|
class JSONFieldWithDefaultTestModel(models.Model):
|
||||||
|
json = JSONField(default={"sukasuka": "YAAAAAZ"})
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
app_label = 'jsonfield'
|
||||||
|
|
||||||
|
|
||||||
|
class BlankJSONFieldTestModel(models.Model):
|
||||||
|
null_json = JSONField(null=True)
|
||||||
|
blank_json = JSONField(blank=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
app_label = 'jsonfield'
|
||||||
|
|
||||||
|
|
||||||
|
class CallableDefaultModel(models.Model):
|
||||||
|
json = JSONField(default=lambda: {'x': 2})
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
app_label = 'jsonfield'
|
|
@ -0,0 +1,163 @@
|
||||||
|
from django.test import TestCase as DjangoTestCase
|
||||||
|
from django.utils.encoding import force_text
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
from jsonfield.tests.jsonfield_test_app.models import * # NOQA
|
||||||
|
from jsonfield.fields import JSONField
|
||||||
|
|
||||||
|
|
||||||
|
class JSONFieldTest(DjangoTestCase):
|
||||||
|
def test_json_field(self):
|
||||||
|
obj = JSONFieldTestModel(json={'spam': 'eggs'})
|
||||||
|
self.assertEqual(obj.json, {'spam': 'eggs'})
|
||||||
|
|
||||||
|
def test_json_field_empty(self):
|
||||||
|
obj = JSONFieldTestModel(json='')
|
||||||
|
self.assertEqual(obj.json, '')
|
||||||
|
|
||||||
|
def test_json_field_null(self):
|
||||||
|
obj = JSONFieldTestModel(json=None)
|
||||||
|
self.assertEqual(obj.json, None)
|
||||||
|
|
||||||
|
def test_json_field_save(self):
|
||||||
|
JSONFieldTestModel.objects.create(
|
||||||
|
id=10,
|
||||||
|
json={'spam': 'eggs'},
|
||||||
|
)
|
||||||
|
obj2 = JSONFieldTestModel.objects.get(id=10)
|
||||||
|
self.assertEqual(obj2.json, {'spam': 'eggs'})
|
||||||
|
|
||||||
|
def test_json_field_save_empty(self):
|
||||||
|
JSONFieldTestModel.objects.create(id=10, json='')
|
||||||
|
obj2 = JSONFieldTestModel.objects.get(id=10)
|
||||||
|
self.assertEqual(obj2.json, '')
|
||||||
|
|
||||||
|
def test_json_field_save_null(self):
|
||||||
|
JSONFieldTestModel.objects.create(id=10, json=None)
|
||||||
|
obj2 = JSONFieldTestModel.objects.get(id=10)
|
||||||
|
self.assertEqual(obj2.json, None)
|
||||||
|
|
||||||
|
def test_db_prep_save(self):
|
||||||
|
field = JSONField("test")
|
||||||
|
field.set_attributes_from_name("json")
|
||||||
|
self.assertEqual(None, field.get_db_prep_save(None, connection=None))
|
||||||
|
self.assertEqual('{"spam": "eggs"}', field.get_db_prep_save({"spam": "eggs"}, connection=None))
|
||||||
|
|
||||||
|
def test_formfield(self):
|
||||||
|
from jsonfield.forms import JSONFormField
|
||||||
|
from jsonfield.widgets import JSONWidget
|
||||||
|
field = JSONField("test")
|
||||||
|
field.set_attributes_from_name("json")
|
||||||
|
formfield = field.formfield()
|
||||||
|
self.assertEqual(type(formfield), JSONFormField)
|
||||||
|
self.assertEqual(type(formfield.widget), JSONWidget)
|
||||||
|
|
||||||
|
def test_formfield_clean_blank(self):
|
||||||
|
field = JSONField("test")
|
||||||
|
formfield = field.formfield()
|
||||||
|
self.assertRaisesMessage(
|
||||||
|
forms.ValidationError,
|
||||||
|
force_text(formfield.error_messages['required']),
|
||||||
|
formfield.clean,
|
||||||
|
value='')
|
||||||
|
|
||||||
|
def test_formfield_clean_none(self):
|
||||||
|
field = JSONField("test")
|
||||||
|
formfield = field.formfield()
|
||||||
|
self.assertRaisesMessage(
|
||||||
|
forms.ValidationError,
|
||||||
|
force_text(formfield.error_messages['required']),
|
||||||
|
formfield.clean,
|
||||||
|
value=None)
|
||||||
|
|
||||||
|
def test_formfield_null_and_blank_clean_blank(self):
|
||||||
|
field = JSONField("test", null=True, blank=True)
|
||||||
|
formfield = field.formfield()
|
||||||
|
self.assertEqual(formfield.clean(value=''), '')
|
||||||
|
|
||||||
|
def test_formfield_null_and_blank_clean_none(self):
|
||||||
|
field = JSONField("test", null=True, blank=True)
|
||||||
|
formfield = field.formfield()
|
||||||
|
self.assertEqual(formfield.clean(value=None), None)
|
||||||
|
|
||||||
|
def test_formfield_blank_clean_blank(self):
|
||||||
|
field = JSONField("test", null=False, blank=True)
|
||||||
|
formfield = field.formfield()
|
||||||
|
self.assertEqual(formfield.clean(value=''), '')
|
||||||
|
|
||||||
|
def test_formfield_blank_clean_none(self):
|
||||||
|
# Hmm, I'm not sure how to do this. What happens if we pass a
|
||||||
|
# None to a field that has null=False?
|
||||||
|
field = JSONField("test", null=False, blank=True)
|
||||||
|
formfield = field.formfield()
|
||||||
|
self.assertEqual(formfield.clean(value=None), None)
|
||||||
|
|
||||||
|
def test_default_value(self):
|
||||||
|
obj = JSONFieldWithDefaultTestModel.objects.create()
|
||||||
|
obj = JSONFieldWithDefaultTestModel.objects.get(id=obj.id)
|
||||||
|
self.assertEqual(obj.json, {'sukasuka': 'YAAAAAZ'})
|
||||||
|
|
||||||
|
def test_query_object(self):
|
||||||
|
JSONFieldTestModel.objects.create(json={})
|
||||||
|
JSONFieldTestModel.objects.create(json={'foo': 'bar'})
|
||||||
|
self.assertEqual(2, JSONFieldTestModel.objects.all().count())
|
||||||
|
self.assertEqual(1, JSONFieldTestModel.objects.exclude(json={}).count())
|
||||||
|
self.assertEqual(1, JSONFieldTestModel.objects.filter(json={}).count())
|
||||||
|
self.assertEqual(1, JSONFieldTestModel.objects.filter(json={'foo': 'bar'}).count())
|
||||||
|
self.assertEqual(1, JSONFieldTestModel.objects.filter(json__contains={'foo': 'bar'}).count())
|
||||||
|
JSONFieldTestModel.objects.create(json={'foo': 'bar', 'baz': 'bing'})
|
||||||
|
self.assertEqual(2, JSONFieldTestModel.objects.filter(json__contains={'foo': 'bar'}).count())
|
||||||
|
# This next one is a bit hard to do without proper lookups, which I'm unlikely to implement.
|
||||||
|
# self.assertEqual(1, JSONFieldTestModel.objects.filter(json__contains={'baz':'bing', 'foo':'bar'}).count())
|
||||||
|
self.assertEqual(2, JSONFieldTestModel.objects.filter(json__contains='foo').count())
|
||||||
|
# This code needs to be implemented!
|
||||||
|
self.assertRaises(TypeError, lambda: JSONFieldTestModel.objects.filter(json__contains=['baz', 'foo']))
|
||||||
|
|
||||||
|
def test_query_isnull(self):
|
||||||
|
JSONFieldTestModel.objects.create(json=None)
|
||||||
|
JSONFieldTestModel.objects.create(json={})
|
||||||
|
JSONFieldTestModel.objects.create(json={'foo': 'bar'})
|
||||||
|
|
||||||
|
self.assertEqual(1, JSONFieldTestModel.objects.filter(json=None).count())
|
||||||
|
self.assertEqual(None, JSONFieldTestModel.objects.get(json=None).json)
|
||||||
|
|
||||||
|
def test_jsonfield_blank(self):
|
||||||
|
BlankJSONFieldTestModel.objects.create(blank_json='', null_json=None)
|
||||||
|
obj = BlankJSONFieldTestModel.objects.get()
|
||||||
|
self.assertEqual(None, obj.null_json)
|
||||||
|
self.assertEqual("", obj.blank_json)
|
||||||
|
obj.save()
|
||||||
|
obj = BlankJSONFieldTestModel.objects.get()
|
||||||
|
self.assertEqual(None, obj.null_json)
|
||||||
|
self.assertEqual("", obj.blank_json)
|
||||||
|
|
||||||
|
def test_callable_default(self):
|
||||||
|
CallableDefaultModel.objects.create()
|
||||||
|
obj = CallableDefaultModel.objects.get()
|
||||||
|
self.assertEqual({'x': 2}, obj.json)
|
||||||
|
|
||||||
|
def test_callable_default_overridden(self):
|
||||||
|
CallableDefaultModel.objects.create(json={'x': 3})
|
||||||
|
obj = CallableDefaultModel.objects.get()
|
||||||
|
self.assertEqual({'x': 3}, obj.json)
|
||||||
|
|
||||||
|
def test_mutable_default_checking(self):
|
||||||
|
obj1 = JSONFieldWithDefaultTestModel()
|
||||||
|
obj2 = JSONFieldWithDefaultTestModel()
|
||||||
|
|
||||||
|
obj1.json['foo'] = 'bar'
|
||||||
|
self.assertNotIn('foo', obj2.json)
|
||||||
|
|
||||||
|
def test_indent(self):
|
||||||
|
JSONField('test', indent=2)
|
||||||
|
|
||||||
|
def test_string_is_not_json_decoded(self):
|
||||||
|
JSONFieldTestModel.objects.create(json='"foo"')
|
||||||
|
self.assertEqual('"foo"', JSONFieldTestModel.objects.get().json)
|
||||||
|
|
||||||
|
|
||||||
|
class SavingModelsTest(DjangoTestCase):
|
||||||
|
def test_saving_null(self):
|
||||||
|
obj = BlankJSONFieldTestModel.objects.create(blank_json='', null_json=None)
|
||||||
|
self.assertEqual('', obj.blank_json)
|
||||||
|
self.assertEqual(None, obj.null_json)
|
|
@ -0,0 +1,62 @@
|
||||||
|
from django.test import TestCase as DjangoTestCase
|
||||||
|
from django.forms import ValidationError
|
||||||
|
|
||||||
|
from jsonfield.forms import JSONFormField
|
||||||
|
from jsonfield.tests.jsonfield_test_app.forms import JSONTestForm
|
||||||
|
|
||||||
|
|
||||||
|
class JSONFormFieldTest(DjangoTestCase):
|
||||||
|
def test_form_field_clean_empty_object(self):
|
||||||
|
field = JSONFormField(required=False)
|
||||||
|
self.assertEqual({}, field.clean('{}'))
|
||||||
|
|
||||||
|
def test_form_field_clean_object(self):
|
||||||
|
field = JSONFormField(required=False)
|
||||||
|
self.assertEqual(
|
||||||
|
{'foo': 'bar', 'baz': 2},
|
||||||
|
field.clean('{"foo":"bar","baz":2}')
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_form_field_widget(self):
|
||||||
|
field = JSONFormField(required=False)
|
||||||
|
self.assertIn(
|
||||||
|
'{\n "a": true\n}',
|
||||||
|
field.widget.render('json', {"a": True})
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_form_field_clean_empty_array(self):
|
||||||
|
field = JSONFormField(required=False)
|
||||||
|
self.assertEqual([], field.clean('[]'))
|
||||||
|
|
||||||
|
def test_required_form_field_array(self):
|
||||||
|
field = JSONFormField(required=True)
|
||||||
|
self.assertEqual([], field.clean('[]'))
|
||||||
|
|
||||||
|
def test_required_form_field_object(self):
|
||||||
|
field = JSONFormField(required=True)
|
||||||
|
self.assertEqual({}, field.clean('{}'))
|
||||||
|
|
||||||
|
def test_required_form_field_empty(self):
|
||||||
|
field = JSONFormField(required=True)
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
field.clean('')
|
||||||
|
|
||||||
|
def test_invalid_json(self):
|
||||||
|
field = JSONFormField(required=True)
|
||||||
|
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
field.clean('{"foo"}')
|
||||||
|
|
||||||
|
|
||||||
|
class JSONFormTest(DjangoTestCase):
|
||||||
|
def test_form_clean(self):
|
||||||
|
form = JSONTestForm({})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
|
||||||
|
|
||||||
|
class JSONFormMultipleSelectFieldTest(DjangoTestCase):
|
||||||
|
def test_multiple_select_data(self):
|
||||||
|
form = JSONTestForm({'json_data': ['SA', 'WA']})
|
||||||
|
assert form.is_valid()
|
||||||
|
|
||||||
|
self.assertEqual(['SA', 'WA'], form.cleaned_data['json_data'])
|
|
@ -0,0 +1,44 @@
|
||||||
|
import datetime
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
|
from django.utils import six
|
||||||
|
|
||||||
|
|
||||||
|
class TZAwareJSONEncoder(DjangoJSONEncoder):
|
||||||
|
def default(self, obj):
|
||||||
|
if isinstance(obj, datetime.datetime):
|
||||||
|
return obj.strftime("%Y-%m-%d %H:%M:%S%z")
|
||||||
|
return super(TZAwareJSONEncoder, self).default(obj)
|
||||||
|
|
||||||
|
|
||||||
|
def default(o):
|
||||||
|
if hasattr(o, 'to_json'):
|
||||||
|
return o.to_json()
|
||||||
|
if isinstance(o, Decimal):
|
||||||
|
return str(o)
|
||||||
|
if isinstance(o, datetime.datetime):
|
||||||
|
if o.tzinfo:
|
||||||
|
return o.strftime('%Y-%m-%dT%H:%M:%S%z')
|
||||||
|
return o.strftime("%Y-%m-%dT%H:%M:%S")
|
||||||
|
if isinstance(o, datetime.date):
|
||||||
|
return o.strftime("%Y-%m-%d")
|
||||||
|
if isinstance(o, datetime.time):
|
||||||
|
if o.tzinfo:
|
||||||
|
return o.strftime('%H:%M:%S%z')
|
||||||
|
return o.strftime("%H:%M:%S")
|
||||||
|
if isinstance(o, set):
|
||||||
|
return list(o)
|
||||||
|
|
||||||
|
raise TypeError(repr(o) + " is not JSON serializable")
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_object_path(dotted_name):
|
||||||
|
if isinstance(dotted_name, six.string_types):
|
||||||
|
path = dotted_name.split('.')
|
||||||
|
module = __import__(dotted_name.rsplit('.', 1)[0])
|
||||||
|
for item in path[1:-1]:
|
||||||
|
module = getattr(module, item)
|
||||||
|
return getattr(module, path[-1])
|
||||||
|
|
||||||
|
return dotted_name
|
|
@ -0,0 +1,20 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.utils import six
|
||||||
|
|
||||||
|
from .utils import default
|
||||||
|
|
||||||
|
|
||||||
|
class JSONWidget(forms.Textarea):
|
||||||
|
def render(self, name, value, attrs=None):
|
||||||
|
if value is None:
|
||||||
|
value = ""
|
||||||
|
if not isinstance(value, six.string_types):
|
||||||
|
value = json.dumps(value, ensure_ascii=False, indent=2,
|
||||||
|
default=default)
|
||||||
|
return super(JSONWidget, self).render(name, value, attrs)
|
||||||
|
|
||||||
|
|
||||||
|
class JSONSelectWidget(forms.SelectMultiple):
|
||||||
|
pass
|
|
@ -0,0 +1,5 @@
|
||||||
|
[egg_info]
|
||||||
|
tag_build =
|
||||||
|
tag_date = 0
|
||||||
|
tag_svn_revision = 0
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import os
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name = "django-jsonfield",
|
||||||
|
version = open(os.path.join(os.path.dirname(__file__), 'jsonfield', 'VERSION')).read().strip(),
|
||||||
|
description = "JSONField for django models",
|
||||||
|
long_description = open("README.rst").read(),
|
||||||
|
url = "http://bitbucket.org/schinckel/django-jsonfield/",
|
||||||
|
author = "Matthew Schinckel",
|
||||||
|
author_email = "matt@schinckel.net",
|
||||||
|
packages = [
|
||||||
|
"jsonfield",
|
||||||
|
],
|
||||||
|
classifiers = [
|
||||||
|
'Programming Language :: Python',
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
|
'License :: OSI Approved :: BSD License',
|
||||||
|
'Operating System :: OS Independent',
|
||||||
|
'Framework :: Django',
|
||||||
|
],
|
||||||
|
test_suite='tests.main',
|
||||||
|
include_package_data=True,
|
||||||
|
)
|
|
@ -0,0 +1,49 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import django
|
||||||
|
|
||||||
|
BASE_PATH = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""
|
||||||
|
Standalone django model test with a 'memory-only-django-installation'.
|
||||||
|
You can play with a django model without a complete django app installation.
|
||||||
|
http://www.djangosnippets.org/snippets/1044/
|
||||||
|
"""
|
||||||
|
os.environ["DJANGO_SETTINGS_MODULE"] = "django.conf.global_settings"
|
||||||
|
from django.conf import global_settings
|
||||||
|
|
||||||
|
global_settings.INSTALLED_APPS = (
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'jsonfield',
|
||||||
|
)
|
||||||
|
global_settings.DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.{DB_ENGINE}'.format(**os.environ),
|
||||||
|
'NAME': 'jsonfield-{DB_NAME}'.format(**os.environ),
|
||||||
|
'USER': os.environ.get('DB_USER', ''),
|
||||||
|
'PASSWORD': os.environ.get('DB_PASSWORD', ''),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global_settings.STATIC_URL = "/static/"
|
||||||
|
global_settings.MEDIA_ROOT = os.path.join(BASE_PATH, 'static')
|
||||||
|
global_settings.STATIC_ROOT = global_settings.MEDIA_ROOT
|
||||||
|
|
||||||
|
global_settings.SECRET_KEY = '334ebe58-a77d-4321-9d01-a7d2cb8d3eea'
|
||||||
|
from django.test.utils import get_runner
|
||||||
|
test_runner = get_runner(global_settings)
|
||||||
|
|
||||||
|
test_runner = test_runner()
|
||||||
|
|
||||||
|
if getattr(django, 'setup', None):
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
failures = test_runner.run_tests(['jsonfield'])
|
||||||
|
|
||||||
|
sys.exit(failures)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in New Issue