summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/internals/deprecation.txt7
-rw-r--r--docs/ref/checks.txt4
-rw-r--r--docs/ref/contrib/postgres/fields.txt95
-rw-r--r--docs/ref/contrib/postgres/forms.txt8
-rw-r--r--docs/ref/databases.txt16
-rw-r--r--docs/ref/forms/fields.txt54
-rw-r--r--docs/ref/models/fields.txt69
-rw-r--r--docs/releases/3.1.txt48
-rw-r--r--docs/topics/db/queries.txt230
9 files changed, 439 insertions, 92 deletions
diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
index 1d89238ede..183ce23408 100644
--- a/docs/internals/deprecation.txt
+++ b/docs/internals/deprecation.txt
@@ -83,6 +83,13 @@ details on these changes.
* ``django.conf.urls.url()`` will be removed.
+* The model ``django.contrib.postgres.fields.JSONField`` will be removed. A
+ stub field will remain for compatibility with historical migrations.
+
+* ``django.contrib.postgres.forms.JSONField``,
+ ``django.contrib.postgres.fields.jsonb.KeyTransform``, and
+ ``django.contrib.postgres.fields.jsonb.KeyTextTransform`` will be removed.
+
See the :ref:`Django 3.1 release notes <deprecated-features-3.1>` for more
details on these changes.
diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt
index daf651392f..37a3a572c9 100644
--- a/docs/ref/checks.txt
+++ b/docs/ref/checks.txt
@@ -190,6 +190,7 @@ Model fields
``<field data type>`` columns.
* **fields.E170**: ``BinaryField``’s ``default`` cannot be a string. Use bytes
content instead.
+* **fields.E180**: ``<database>`` does not support ``JSONField``\s.
* **fields.E900**: ``IPAddressField`` has been removed except for support in
historical migrations.
* **fields.W900**: ``IPAddressField`` has been deprecated. Support for it
@@ -204,6 +205,9 @@ Model fields
Django 3.1. *This check appeared in Django 2.2 and 3.0*.
* **fields.W903**: ``NullBooleanField`` is deprecated. Support for it (except
in historical migrations) will be removed in Django 4.0.
+* **fields.W904**: ``django.contrib.postgres.fields.JSONField`` is deprecated.
+ Support for it (except in historical migrations) will be removed in Django
+ 4.0.
File fields
~~~~~~~~~~~
diff --git a/docs/ref/contrib/postgres/fields.txt b/docs/ref/contrib/postgres/fields.txt
index baebba9c50..aeacc72e7c 100644
--- a/docs/ref/contrib/postgres/fields.txt
+++ b/docs/ref/contrib/postgres/fields.txt
@@ -16,8 +16,7 @@ Indexes such as :class:`~django.contrib.postgres.indexes.GinIndex` and
:class:`~django.contrib.postgres.indexes.GistIndex` are better suited, though
the index choice is dependent on the queries that you're using. Generally, GiST
may be a good choice for the :ref:`range fields <range-fields>` and
-:class:`HStoreField`, and GIN may be helpful for :class:`ArrayField` and
-:class:`JSONField`.
+:class:`HStoreField`, and GIN may be helpful for :class:`ArrayField`.
``ArrayField``
==============
@@ -517,96 +516,14 @@ using in conjunction with lookups on
of the JSON which allows indexing. The trade-off is a small additional cost
on writing to the ``jsonb`` field. ``JSONField`` uses ``jsonb``.
-Querying ``JSONField``
-----------------------
-
-We will use the following example model::
-
- from django.contrib.postgres.fields import JSONField
- from django.db import models
-
- class Dog(models.Model):
- name = models.CharField(max_length=200)
- data = JSONField()
-
- def __str__(self):
- return self.name
-
-.. fieldlookup:: jsonfield.key
-
-Key, index, and path lookups
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To query based on a given dictionary key, use that key as the lookup name::
-
- >>> Dog.objects.create(name='Rufus', data={
- ... 'breed': 'labrador',
- ... 'owner': {
- ... 'name': 'Bob',
- ... 'other_pets': [{
- ... 'name': 'Fishy',
- ... }],
- ... },
- ... })
- >>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': None})
-
- >>> Dog.objects.filter(data__breed='collie')
- <QuerySet [<Dog: Meg>]>
-
-Multiple keys can be chained together to form a path lookup::
+.. deprecated:: 3.1
- >>> Dog.objects.filter(data__owner__name='Bob')
- <QuerySet [<Dog: Rufus>]>
+ Use :class:`django.db.models.JSONField` instead.
-If the key is an integer, it will be interpreted as an index lookup in an
-array::
-
- >>> Dog.objects.filter(data__owner__other_pets__0__name='Fishy')
- <QuerySet [<Dog: Rufus>]>
-
-If the key you wish to query by clashes with the name of another lookup, use
-the :lookup:`jsonfield.contains` lookup instead.
-
-If only one key or index is used, the SQL operator ``->`` is used. If multiple
-operators are used then the ``#>`` operator is used.
-
-To query for ``null`` in JSON data, use ``None`` as a value::
-
- >>> Dog.objects.filter(data__owner=None)
- <QuerySet [<Dog: Meg>]>
-
-To query for missing keys, use the ``isnull`` lookup::
-
- >>> Dog.objects.create(name='Shep', data={'breed': 'collie'})
- >>> Dog.objects.filter(data__owner__isnull=True)
- <QuerySet [<Dog: Shep>]>
-
-.. warning::
-
- Since any string could be a key in a JSON object, any lookup other than
- those listed below will be interpreted as a key lookup. No errors are
- raised. Be extra careful for typing mistakes, and always check your queries
- work as you intend.
-
-Containment and key operations
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. fieldlookup:: jsonfield.contains
-.. fieldlookup:: jsonfield.contained_by
-.. fieldlookup:: jsonfield.has_key
-.. fieldlookup:: jsonfield.has_any_keys
-.. fieldlookup:: jsonfield.has_keys
-
-:class:`~django.contrib.postgres.fields.JSONField` shares lookups relating to
-containment and keys with :class:`~django.contrib.postgres.fields.HStoreField`.
+Querying ``JSONField``
+----------------------
-- :lookup:`contains <hstorefield.contains>` (accepts any JSON rather than
- just a dictionary of strings)
-- :lookup:`contained_by <hstorefield.contained_by>` (accepts any JSON
- rather than just a dictionary of strings)
-- :lookup:`has_key <hstorefield.has_key>`
-- :lookup:`has_any_keys <hstorefield.has_any_keys>`
-- :lookup:`has_keys <hstorefield.has_keys>`
+See :ref:`querying-jsonfield` for details.
.. _range-fields:
diff --git a/docs/ref/contrib/postgres/forms.txt b/docs/ref/contrib/postgres/forms.txt
index f559ac75cb..14a3ad61de 100644
--- a/docs/ref/contrib/postgres/forms.txt
+++ b/docs/ref/contrib/postgres/forms.txt
@@ -164,8 +164,8 @@ Fields
.. class:: JSONField
A field which accepts JSON encoded data for a
- :class:`~django.contrib.postgres.fields.JSONField`. It is represented by an
- HTML ``<textarea>``.
+ :class:`~django.db.models.JSONField`. It is represented by an HTML
+ ``<textarea>``.
.. admonition:: User friendly forms
@@ -173,6 +173,10 @@ Fields
it is a useful way to format data from a client-side widget for
submission to the server.
+ .. deprecated:: 3.1
+
+ Use :class:`django.forms.JSONField` instead.
+
Range Fields
------------
diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt
index f01a054d51..a16f525d96 100644
--- a/docs/ref/databases.txt
+++ b/docs/ref/databases.txt
@@ -783,6 +783,22 @@ iterator. Your code must handle this.
.. _`Isolation in SQLite`: https://sqlite.org/isolation.html
+.. _sqlite-json1:
+
+Enabling JSON1 extension on SQLite
+----------------------------------
+
+To use :class:`~django.db.models.JSONField` on SQLite, you need to enable the
+`JSON1 extension`_ on Python's :py:mod:`sqlite3` library. If the extension is
+not enabled on your installation, a system error (``fields.E180``) will be
+raised.
+
+To enable the JSON1 extension you can follow the instruction on
+`the wiki page`_.
+
+.. _JSON1 extension: https://www.sqlite.org/json1.html
+.. _the wiki page: https://code.djangoproject.com/wiki/JSON1Extension
+
.. _oracle-notes:
Oracle notes
diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt
index 3d228e88ad..58db957512 100644
--- a/docs/ref/forms/fields.txt
+++ b/docs/ref/forms/fields.txt
@@ -776,6 +776,60 @@ For each field, we describe the default widget used if you don't specify
These control the range of values permitted in the field.
+``JSONField``
+-------------
+
+.. class:: JSONField(encoder=None, decoder=None, **kwargs)
+
+ .. versionadded:: 3.1
+
+ A field which accepts JSON encoded data for a
+ :class:`~django.db.models.JSONField`.
+
+ * Default widget: :class:`Textarea`
+ * Empty value: ``''`` (an empty string)
+ * Normalizes to: A Python representation of the JSON value (usually as a
+ ``dict``, ``list``, or ``None``), depending on :attr:`JSONField.decoder`.
+ * Validates that the given value is a valid JSON.
+ * Error message keys: ``required``, ``invalid``
+
+ Takes two optional arguments:
+
+ .. attribute:: encoder
+
+ A :py:class:`json.JSONEncoder` subclass to serialize data types not
+ supported by the standard JSON serializer (e.g. ``datetime.datetime``
+ or :class:`~python:uuid.UUID`). For example, you can use the
+ :class:`~django.core.serializers.json.DjangoJSONEncoder` class.
+
+ Defaults to ``json.JSONEncoder``.
+
+ .. attribute:: decoder
+
+ A :py:class:`json.JSONDecoder` subclass to deserialize the input. Your
+ deserialization may need to account for the fact that you can't be
+ certain of the input type. For example, you run the risk of returning a
+ ``datetime`` that was actually a string that just happened to be in the
+ same format chosen for ``datetime``\s.
+
+ The ``decoder`` can be used to validate the input. If
+ :py:class:`json.JSONDecodeError` is raised during the deserialization,
+ a ``ValidationError`` will be raised.
+
+ Defaults to ``json.JSONDecoder``.
+
+ .. note::
+
+ If you use a :class:`ModelForm <django.forms.ModelForm>`, the
+ ``encoder`` and ``decoder`` from :class:`~django.db.models.JSONField`
+ will be used.
+
+ .. admonition:: User friendly forms
+
+ ``JSONField`` is not particularly user friendly in most cases. However,
+ it is a useful way to format data from a client-side widget for
+ submission to the server.
+
``GenericIPAddressField``
-------------------------
diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
index 35b83c9b7a..452736dfa8 100644
--- a/docs/ref/models/fields.txt
+++ b/docs/ref/models/fields.txt
@@ -348,7 +348,7 @@ The default can't be a mutable object (model instance, ``list``, ``set``, etc.),
as a reference to the same instance of that object would be used as the default
value in all new model instances. Instead, wrap the desired default in a
callable. For example, if you want to specify a default ``dict`` for
-:class:`~django.contrib.postgres.fields.JSONField`, use a function::
+:class:`~django.db.models.JSONField`, use a function::
def contact_default():
return {"email": "to1@example.com"}
@@ -1175,6 +1175,73 @@ are converted to lowercase.
If you allow for blank values, you have to allow for null values since blank
values are stored as null.
+``JSONField``
+-------------
+
+.. class:: JSONField(encoder=None, decoder=None, **options)
+
+.. versionadded:: 3.1
+
+A field for storing JSON encoded data. In Python the data is represented in its
+Python native format: dictionaries, lists, strings, numbers, booleans and
+``None``.
+
+``JSONField`` is supported on MariaDB 10.2.7+, MySQL 5.7.8+, Oracle,
+PostgreSQL, and SQLite 3.9.0+ (with the :ref:`JSON1 extension enabled
+<sqlite-json1>`).
+
+.. attribute:: JSONField.encoder
+
+ An optional :py:class:`json.JSONEncoder` subclass to serialize data types
+ not supported by the standard JSON serializer (e.g. ``datetime.datetime``
+ or :class:`~python:uuid.UUID`). For example, you can use the
+ :class:`~django.core.serializers.json.DjangoJSONEncoder` class.
+
+ Defaults to ``json.JSONEncoder``.
+
+.. attribute:: JSONField.decoder
+
+ An optional :py:class:`json.JSONDecoder` subclass to deserialize the value
+ retrieved from the database. The value will be in the format chosen by the
+ custom encoder (most often a string). Your deserialization may need to
+ account for the fact that you can't be certain of the input type. For
+ example, you run the risk of returning a ``datetime`` that was actually a
+ string that just happened to be in the same format chosen for
+ ``datetime``\s.
+
+ Defaults to ``json.JSONDecoder``.
+
+If you give the field a :attr:`~django.db.models.Field.default`, ensure it's an
+immutable object, such as a ``str``, or a callable object that returns a fresh
+mutable object each time, such as ``dict`` or a function. Providing a mutable
+default object like ``default={}`` or ``default=[]`` shares the one object
+between all model instances.
+
+To query ``JSONField`` in the database, see :ref:`querying-jsonfield`.
+
+.. admonition:: Indexing
+
+ :class:`~django.db.models.Index` and :attr:`.Field.db_index` both create a
+ B-tree index, which isn't particularly helpful when querying ``JSONField``.
+ On PostgreSQL only, you can use
+ :class:`~django.contrib.postgres.indexes.GinIndex` that is better suited.
+
+.. admonition:: PostgreSQL users
+
+ PostgreSQL has two native JSON based data types: ``json`` and ``jsonb``.
+ The main difference between them is how they are stored and how they can be
+ queried. PostgreSQL's ``json`` field is stored as the original string
+ representation of the JSON and must be decoded on the fly when queried
+ based on keys. The ``jsonb`` field is stored based on the actual structure
+ of the JSON which allows indexing. The trade-off is a small additional cost
+ on writing to the ``jsonb`` field. ``JSONField`` uses ``jsonb``.
+
+.. admonition:: Oracle users
+
+ Oracle Database does not support storing JSON scalar values. Only JSON
+ objects and arrays (represented in Python using :py:class:`dict` and
+ :py:class:`list`) are supported.
+
``NullBooleanField``
--------------------
diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt
index a480cbfa57..120a326628 100644
--- a/docs/releases/3.1.txt
+++ b/docs/releases/3.1.txt
@@ -64,6 +64,21 @@ Asynchronous support should be entirely backwards-compatible and we have tried
to ensure that it has no speed regressions for your existing, synchronous code.
It should have no noticeable effect on any existing Django projects.
+JSONField for all supported database backends
+---------------------------------------------
+
+Django now includes the :class:`.models.JSONField` and
+:class:`forms.JSONField <django.forms.JSONField>` that can be used on all
+supported database backends. Both fields support the use of custom JSON
+encoders and decoders. The model field supports the introspection, lookups, and
+transforms that were previously PostgreSQL-only.
+
+If your project uses ``django.contrib.postgres.fields.JSONField``, plus the
+related form field and transforms, you should adjust to use the new fields,
+and generate and apply a database migration. For now, the old fields and
+transforms are left as a reference to the new ones and are :ref:`deprecated as
+of this release <deprecated-jsonfield>`.
+
Minor features
--------------
@@ -549,6 +564,15 @@ backends.
``DatabaseOperations.execute_sql_flush()`` is removed. The method now uses
the database of the called instance.
+* Third-party database backends must implement support for ``JSONField`` or set
+ ``DatabaseFeatures.supports_json_field`` to ``False``. If storing primitives
+ is not supported, set ``DatabaseFeatures.supports_primitives_in_json_field``
+ to ``False``. If there is a true datatype for JSON, set
+ ``DatabaseFeatures.has_native_json_field`` to ``True``.
+
+* Third party database backends must implement introspection for ``JSONField``
+ or set ``can_introspect_json_field`` to ``False``.
+
Dropped support for MariaDB 10.1
--------------------------------
@@ -693,11 +717,35 @@ Miscellaneous
* The minimum supported version of ``mysqlclient`` is increased from 1.3.13 to
1.4.0.
+* The undocumented ``django.contrib.postgres.forms.InvalidJSONInput`` and
+ ``django.contrib.postgres.forms.JSONString`` are moved to
+ ``django.forms.fields``.
+
+* The undocumented ``django.contrib.postgres.fields.jsonb.JsonAdapter`` class
+ is removed.
+
.. _deprecated-features-3.1:
Features deprecated in 3.1
==========================
+.. _deprecated-jsonfield:
+
+PostgreSQL ``JSONField``
+------------------------
+
+``django.contrib.postgres.fields.JSONField`` and
+``django.contrib.postgres.forms.JSONField`` are deprecated in favor of
+:class:`.models.JSONField` and
+:class:`forms.JSONField <django.forms.JSONField>`.
+
+The undocumented ``django.contrib.postgres.fields.jsonb.KeyTransform`` and
+``django.contrib.postgres.fields.jsonb.KeyTextTransform`` are also deprecated
+in favor of the transforms in ``django.db.models.fields.json``.
+
+The new ``JSONField``\s, ``KeyTransform``, and ``KeyTextTransform`` can be used
+on all supported database backends.
+
Miscellaneous
-------------
diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt
index c0d0f2b3e3..c0e141ea8d 100644
--- a/docs/topics/db/queries.txt
+++ b/docs/topics/db/queries.txt
@@ -798,6 +798,236 @@ being evaluated and therefore populate the cache::
Simply printing the queryset will not populate the cache. This is because
the call to ``__repr__()`` only returns a slice of the entire queryset.
+.. _querying-jsonfield:
+
+Querying ``JSONField``
+======================
+
+Lookups implementation is different in :class:`~django.db.models.JSONField`,
+mainly due to the existence of key transformations. To demonstrate, we will use
+the following example model::
+
+ from django.db import models
+
+ class Dog(models.Model):
+ name = models.CharField(max_length=200)
+ data = models.JSONField(null=True)
+
+ def __str__(self):
+ return self.name
+
+Storing and querying for ``None``
+---------------------------------
+
+As with other fields, storing ``None`` as the field's value will store it as
+SQL ``NULL``. While not recommended, it is possible to store JSON scalar
+``null`` instead of SQL ``NULL`` by using :class:`Value('null')
+<django.db.models.Value>`.
+
+Whichever of the values is stored, when retrieved from the database, the Python
+representation of the JSON scalar ``null`` is the same as SQL ``NULL``, i.e.
+``None``. Therefore, it can be hard to distinguish between them.
+
+This only applies to ``None`` as the top-level value of the field. If ``None``
+is inside a :py:class:`list` or :py:class:`dict`, it will always be interpreted
+as JSON ``null``.
+
+When querying, ``None`` value will always be interpreted as JSON ``null``. To
+query for SQL ``NULL``, use :lookup:`isnull`::
+
+ >>> Dog.objects.create(name='Max', data=None) # SQL NULL.
+ <Dog: Max>
+ >>> Dog.objects.create(name='Archie', data=Value('null')) # JSON null.
+ <Dog: Archie>
+ >>> Dog.objects.filter(data=None)
+ <QuerySet [<Dog: Archie>]>
+ >>> Dog.objects.filter(data=Value('null'))
+ <QuerySet [<Dog: Archie>]>
+ >>> Dog.objects.filter(data__isnull=True)
+ <QuerySet [<Dog: Max>]>
+ >>> Dog.objects.filter(data__isnull=False)
+ <QuerySet [<Dog: Archie>]>
+
+Unless you are sure you wish to work with SQL ``NULL`` values, consider setting
+``null=False`` and providing a suitable default for empty values, such as
+``default=dict``.
+
+.. note::
+
+ Storing JSON scalar ``null`` does not violate :attr:`null=False
+ <django.db.models.Field.null>`.
+
+.. fieldlookup:: jsonfield.key
+
+Key, index, and path transforms
+-------------------------------
+
+To query based on a given dictionary key, use that key as the lookup name::
+
+ >>> Dog.objects.create(name='Rufus', data={
+ ... 'breed': 'labrador',
+ ... 'owner': {
+ ... 'name': 'Bob',
+ ... 'other_pets': [{
+ ... 'name': 'Fishy',
+ ... }],
+ ... },
+ ... })
+ <Dog: Rufus>
+ >>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': None})
+ <Dog: Meg>
+ >>> Dog.objects.filter(data__breed='collie')
+ <QuerySet [<Dog: Meg>]>
+
+Multiple keys can be chained together to form a path lookup::
+
+ >>> Dog.objects.filter(data__owner__name='Bob')
+ <QuerySet [<Dog: Rufus>]>
+
+If the key is an integer, it will be interpreted as an index transform in an
+array::
+
+ >>> Dog.objects.filter(data__owner__other_pets__0__name='Fishy')
+ <QuerySet [<Dog: Rufus>]>
+
+If the key you wish to query by clashes with the name of another lookup, use
+the :lookup:`contains <jsonfield.contains>` lookup instead.
+
+To query for missing keys, use the ``isnull`` lookup::
+
+ >>> Dog.objects.create(name='Shep', data={'breed': 'collie'})
+ <Dog: Shep>
+ >>> Dog.objects.filter(data__owner__isnull=True)
+ <QuerySet [<Dog: Shep>]>
+
+.. note::
+
+ The lookup examples given above implicitly use the :lookup:`exact` lookup.
+ Key, index, and path transforms can also be chained with:
+ :lookup:`contains`, :lookup:`icontains`, :lookup:`endswith`,
+ :lookup:`iendswith`, :lookup:`iexact`, :lookup:`regex`, :lookup:`iregex`,
+ :lookup:`startswith`, :lookup:`istartswith`, :lookup:`lt`, :lookup:`lte`,
+ :lookup:`gt`, and :lookup:`gte` lookups.
+
+.. warning::
+
+ Since any string could be a key in a JSON object, any lookup other than
+ those listed below will be interpreted as a key lookup. No errors are
+ raised. Be extra careful for typing mistakes, and always check your queries
+ work as you intend.
+
+.. admonition:: MariaDB and Oracle users
+
+ Using :meth:`~django.db.models.query.QuerySet.order_by` on key, index, or
+ path transforms will sort the objects using the string representation of
+ the values. This is because MariaDB and Oracle Database do not provide a
+ function that converts JSON values into their equivalent SQL values.
+
+.. admonition:: Oracle users
+
+ On Oracle Database, using ``None`` as the lookup value in an
+ :meth:`~django.db.models.query.QuerySet.exclude` query will return objects
+ that do not have ``null`` as the value at the given path, including objects
+ that do not have the path. On other database backends, the query will
+ return objects that have the path and the value is not ``null``.
+
+.. admonition:: PostgreSQL users
+
+ On PostgreSQL, if only one key or index is used, the SQL operator ``->`` is
+ used. If multiple operators are used then the ``#>`` operator is used.
+
+Containment and key operations
+------------------------------
+
+.. fieldlookup:: jsonfield.contains
+
+``contains``
+~~~~~~~~~~~~
+
+The :lookup:`contains` lookup is overridden on ``JSONField``. The returned
+objects are those where the given ``dict`` of key-value pairs are all
+contained in the top-level of the field. For example::
+
+ >>> Dog.objects.create(name='Rufus', data={'breed': 'labrador', 'owner': 'Bob'})
+ <Dog: Rufus>
+ >>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
+ <Dog: Meg>
+ >>> Dog.objects.create(name='Fred', data={})
+ <Dog: Fred>
+ >>> Dog.objects.filter(data__contains={'owner': 'Bob'})
+ <QuerySet [<Dog: Rufus>, <Dog: Meg>]>
+ >>> Dog.objects.filter(data__contains={'breed': 'collie'})
+ <QuerySet [<Dog: Meg>]>
+
+.. fieldlookup:: jsonfield.contained_by
+
+``contained_by``
+~~~~~~~~~~~~~~~~
+
+This is the inverse of the :lookup:`contains <jsonfield.contains>` lookup - the
+objects returned will be those where the key-value pairs on the object are a
+subset of those in the value passed. For example::
+
+ >>> Dog.objects.create(name='Rufus', data={'breed': 'labrador', 'owner': 'Bob'})
+ <Dog: Rufus>
+ >>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
+ <Dog: Meg>
+ >>> Dog.objects.create(name='Fred', data={})
+ <Dog: Fred>
+ >>> Dog.objects.filter(data__contained_by={'breed': 'collie', 'owner': 'Bob'})
+ <QuerySet [<Dog: Meg>, <Dog: Fred>]>
+ >>> Dog.objects.filter(data__contained_by={'breed': 'collie'})
+ <QuerySet [<Dog: Fred>]>
+
+.. admonition:: Oracle
+
+ ``contained_by`` is not supported on Oracle.
+
+.. fieldlookup:: jsonfield.has_key
+
+``has_key``
+~~~~~~~~~~~
+
+Returns objects where the given key is in the top-level of the data. For
+example::
+
+ >>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
+ <Dog: Rufus>
+ >>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
+ <Dog: Meg>
+ >>> Dog.objects.filter(data__has_key='owner')
+ <QuerySet [<Dog: Meg>]>
+
+.. fieldlookup:: jsonfield.has_any_keys
+
+``has_keys``
+~~~~~~~~~~~~
+
+Returns objects where all of the given keys are in the top-level of the data.
+For example::
+
+ >>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
+ <Dog: Rufus>
+ >>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
+ <Dog: Meg>
+ >>> Dog.objects.filter(data__has_keys=['breed', 'owner'])
+ <QuerySet [<Dog: Meg>]>
+
+.. fieldlookup:: jsonfield.has_keys
+
+``has_any_keys``
+~~~~~~~~~~~~~~~~
+
+Returns objects where any of the given keys are in the top-level of the data.
+For example::
+
+ >>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
+ <Dog: Rufus>
+ >>> Dog.objects.create(name='Meg', data={'owner': 'Bob'})
+ <Dog: Meg>
+ >>> Dog.objects.filter(data__has_any_keys=['owner', 'breed'])
+ <QuerySet [<Dog: Rufus>, <Dog: Meg>]>
+
.. _complex-lookups-with-q:
Complex lookups with ``Q`` objects