diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/internals/deprecation.txt | 7 | ||||
| -rw-r--r-- | docs/ref/checks.txt | 4 | ||||
| -rw-r--r-- | docs/ref/contrib/postgres/fields.txt | 95 | ||||
| -rw-r--r-- | docs/ref/contrib/postgres/forms.txt | 8 | ||||
| -rw-r--r-- | docs/ref/databases.txt | 16 | ||||
| -rw-r--r-- | docs/ref/forms/fields.txt | 54 | ||||
| -rw-r--r-- | docs/ref/models/fields.txt | 69 | ||||
| -rw-r--r-- | docs/releases/3.1.txt | 48 | ||||
| -rw-r--r-- | docs/topics/db/queries.txt | 230 |
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 |
