Skip to content

Commit bcd7870

Browse files
timgrahamaclark4life
authored andcommitted
combine topic guide with howto
1 parent 7ae79b8 commit bcd7870

File tree

5 files changed

+188
-218
lines changed

5 files changed

+188
-218
lines changed

docs/howto/queryable-encryption.rst

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,103 @@ Then in your Django settings, add the custom database router to the
122122
"myapp.routers.EncryptedRouter",
123123
]
124124

125+
Encrypted fields
126+
================
127+
128+
Now you can start using encrypted fields in your Django models.
129+
130+
:doc:`Encrypted fields </ref/models/encrypted-fields>` may be used to protect
131+
sensitive data like social security numbers, credit card information, or
132+
personal health information. With Queryable Encryption, you can also perform
133+
queries on encrypted fields. To use encrypted fields in your models,
134+
import the necessary field types from ``django_mongodb_backend.models`` and
135+
define your models as usual.
136+
137+
Here are models based on the `Python Queryable Encryption Tutorial`_::
138+
139+
# myapp/models.py
140+
from django.db import models
141+
from django_mongodb_backend.models import EmbeddedModel
142+
from django_mongodb_backend.fields import (
143+
EmbeddedModelField,
144+
EncryptedCharField,
145+
EncryptedEmbeddedModelField,
146+
)
147+
148+
149+
class PatientRecord(EmbeddedModel):
150+
ssn = EncryptedCharField(max_length=11, queries={"queryType": "equality"})
151+
billing = EncryptedEmbeddedModelField("Billing")
152+
bill_amount = models.DecimalField(max_digits=10, decimal_places=2)
153+
154+
class Patient(models.Model):
155+
patient_name = models.CharField(max_length=255)
156+
patient_id = models.BigIntegerField()
157+
patient_record = EmbeddedModelField("PatientRecord")
158+
159+
def __str__(self):
160+
return f"{self.patient_name} ({self.patient_id})"
161+
162+
class Billing(EmbeddedModel):
163+
cc_type = models.CharField(max_length=50)
164+
cc_number = models.CharField(max_length=20)
165+
166+
.. _Python Queryable Encryption Tutorial: https://github.com/mongodb/docs/tree/main/content/manual/manual/source/includes/qe-tutorials/python
167+
168+
.. _qe-migrations:
169+
170+
Migrations
171+
==========
172+
173+
Once you have defined your models, create a migration as usual:
174+
175+
.. code-block:: console
176+
177+
$ python manage.py makemigrations
178+
179+
Then run the migrations with:
180+
181+
.. code-block:: console
182+
183+
$ python manage.py migrate --database encrypted
184+
185+
.. warning::
186+
187+
Be aware that you cannot add encrypted fields to existing models, nor can
188+
you change the definition of an encrypted field, for example, to make it
189+
queryable.
190+
191+
Now create and manipulate instances of the data just like any other Django
192+
model data. The fields will automatically handle encryption and decryption,
193+
ensuring that :ref:`sensitive data is stored securely in the database
194+
<manual:qe-features-encryption-at-rest>`.
195+
196+
Querying encrypted fields
197+
=========================
198+
199+
In order to query encrypted fields, you must include the :ref:`queries
200+
<encrypted-fields-queries>` argument. For example, notice ``PatientRecord``\'s
201+
``ssn`` field::
202+
203+
class PatientRecord(EmbeddedModel):
204+
ssn = EncryptedCharField(max_length=11, queries={"queryType": "equality"})
205+
206+
You can perform a equality query just like you would on a non-encrypted field:
207+
208+
.. code-block:: pycon
209+
210+
>>> patient = Patient.objects.get(patient_record__ssn="123-45-6789")
211+
>>> patient.name
212+
'John Doe'
213+
125214
.. _qe-configuring-kms:
126215

127216
Configuring the Key Management Service (KMS)
128217
============================================
129218

130219
A local KMS provider with a hardcoded key is suitable for local development and
131-
testing, but production environment, you should securely :ref:`store and manage your
132-
encryption keys <manual:qe-fundamentals-kms-providers>`.
220+
testing, but production environment, you should securely :ref:`store and manage
221+
your encryption keys <manual:qe-fundamentals-kms-providers>`.
133222

134223
To use Queryable Encryption, you must configure a Key Management Service (KMS)
135224
to store and manage the encryption keys used to encrypt and decrypt data.
@@ -294,6 +383,3 @@ To configure it in your Django settings, use
294383
environment variable in your shell before starting your Django application::
295384

296385
$ export DYLD_FALLBACK_LIBRARY_PATH="/path/to/mongo_crypt_shared/:$DYLD_FALLBACK_LIBRARY_PATH"
297-
298-
You are now ready to :doc:`start developing applications
299-
</topics/queryable-encryption>` with Queryable Encryption!

docs/index.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ Models
5151
**Topic guides:**
5252

5353
- :doc:`topics/embedded-models`
54-
- :doc:`topics/queryable-encryption`
5554
- :doc:`topics/transactions`
5655

5756
Forms

docs/ref/models/encrypted-fields.rst

Lines changed: 97 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,12 @@
22
Encrypted fields
33
================
44

5-
.. versionadded:: 5.2.3
6-
7-
Django MongoDB Backend supports :doc:`manual:core/queryable-encryption`.
5+
.. currentmodule:: django_mongodb_backend.fields
86

9-
See :doc:`/howto/queryable-encryption` for more information on how to use
10-
Queryable Encryption with Django MongoDB Backend.
7+
.. versionadded:: 5.2.3
118

12-
See the :doc:`/topics/queryable-encryption` topic guide for
13-
more information on developing applications with Queryable Encryption.
9+
To use encrypted fields, you must :doc:`configure Queryable Encryption
10+
</howto/queryable-encryption>`.
1411

1512
The following tables detailed which fields have encrypted counterparts. In all
1613
cases, the encrypted field names are simply prefixed with ``Encrypted``, e.g.
@@ -48,19 +45,62 @@ cases, the encrypted field names are simply prefixed with ``Encrypted``, e.g.
4845
.. csv-table:: ``django_mongodb_backend.fields``
4946
:header: "Model Field", "Encrypted version available?"
5047

51-
:class:`~.fields.ArrayField`, Yes
52-
:class:`~.fields.EmbeddedModelArrayField`, Yes
53-
:class:`~.fields.EmbeddedModelField`, Yes
54-
:class:`~.fields.ObjectIdField`, Yes
55-
:class:`~.fields.PolymorphicEmbeddedModelField`, No: may be implemented in the future.
56-
:class:`~.fields.PolymorphicEmbeddedModelArrayField`, No: may be implemented in the future.
48+
:class:`ArrayField`, Yes
49+
:class:`EmbeddedModelArrayField`, Yes
50+
:class:`EmbeddedModelField`, Yes
51+
:class:`ObjectIdField`, Yes
52+
:class:`PolymorphicEmbeddedModelField`, No: may be implemented in the future.
53+
:class:`PolymorphicEmbeddedModelArrayField`, No: may be implemented in the future.
54+
55+
.. _encrypted-fields-queries:
56+
57+
``EncryptedField.queries``
58+
--------------------------
59+
60+
Most encrypted fields* take an optional ``queries`` argument. It's a dictionary
61+
that specifies the type of queries that can be performed on the field, as well
62+
as any query options.
5763

58-
These fields don't support the ``queries`` argument:
64+
The :ref:`available query types <manual:qe-fundamentals-encrypt-query>` depend
65+
on your version of MongoDB. For example, in MongoDB 8.0, the supported types
66+
are ``equality`` and ``range``.
67+
68+
.. admonition:: Query types vs. Django lookups
69+
70+
Range queries in Queryable Encryption are different from Django's
71+
:ref:`range lookups <django:field-lookups>`. Range queries allow you to
72+
perform comparisons on encrypted fields, while Django's range lookups are
73+
used for filtering based on a range of values.
74+
75+
\* These fields don't support the ``queries`` argument:
5976

6077
- ``EncryptedArrayField``
6178
- ``EncryptedEmbeddedModelArrayField``
6279
- ``EncryptedEmbeddedModelField``
6380

81+
Embedded model encryption
82+
=========================
83+
84+
There are two ways to encrypt embedded models. You can either encrypt the
85+
entire subdocument, in which case you can't query any the subdocuments fields,
86+
or you can encrypt only selected fields of the subdocument.
87+
88+
Encrypting the entire subdocument
89+
---------------------------------
90+
91+
To encrypt a subdocument, use ``EncryptedEmbeddedModelField`` or
92+
``EncryptedEmbeddedModelArrayField``. In this case, the field's embedded model
93+
cannot have any encrypted fields.
94+
95+
Encrypting selected fields of a subdocument
96+
-------------------------------------------
97+
98+
To encrypt only select fields of a subdocument, use :class:`EmbeddedModelField`
99+
and any of the other encrypted fields on the embedded model.
100+
101+
MongoDB doesn't support encrypting selected fields of
102+
``EmbeddedModelArrayField``.
103+
64104
Limitations
65105
===========
66106

@@ -70,20 +110,54 @@ MongoDB imposes some restrictions on encrypted fields:
70110
* They cannot be part of a unique constraint.
71111
* They cannot be null.
72112

113+
``QuerySet`` limitations
114+
------------------------
115+
116+
In addition to :ref:`Django MongoDB Backend's QuerySet limitations
117+
<known-issues-limitations-querying>`, some ``QuerySet`` methods aren't
118+
supported on encrypted fields. Each unsupported method is followed by a sample
119+
error message from the database. Depending on the exact query, error messages
120+
may vary.
121+
122+
- :meth:`~django.db.models.query.QuerySet.order_by`: Cannot add an encrypted
123+
field as a prefix of another encrypted field.
124+
- :meth:`~django.db.models.query.QuerySet.alias`,
125+
:meth:`~django.db.models.query.QuerySet.annotate`,
126+
:meth:`~django.db.models.query.QuerySet.distinct`: Cannot group on field
127+
'_id.value' which is encrypted with the random algorithm or whose encryption
128+
properties are not known until runtime.
129+
- :meth:`~django.db.models.query.QuerySet.dates`,
130+
:meth:`~django.db.models.query.QuerySet.datetimes`: If the value type is a
131+
date, the type of the index must also be date (and vice versa).
132+
- :meth:`~django.db.models.query.QuerySet.in_bulk`: Encrypted fields can't have
133+
unique constraints.
134+
135+
# TODO: add details about joined queries after
136+
https://github.com/mongodb/django-mongodb-backend/pull/443 is finalized.
137+
138+
There are also several ``QuerySet`` methods that aren't permitted on any models
139+
(regardless of whether or not they have encrypted fields) that use a database
140+
connection with Automatic Encryption. Each unsupported method is followed by a
141+
sample error message from the database.
142+
143+
- :meth:`~django.db.models.query.QuerySet.update`: Multi-document updates are
144+
not allowed with Queryable Encryption.
145+
- :meth:`~django.db.models.query.QuerySet.aggregate`,
146+
:meth:`~django.db.models.query.QuerySet.count`: Aggregation stage
147+
$internalFacetTeeConsumer is not allowed or supported with automatic
148+
encryption.
149+
- :meth:`~django.db.models.query.QuerySet.union`: Aggregation stage $unionWith
150+
is not allowed or supported with automatic encryption.
151+
73152
``EncryptedFieldMixin``
74153
=======================
75154

76155
.. class:: EncryptedFieldMixin
77156

78157
.. versionadded:: 5.2.3
79158

80-
A mixin that can be used to create custom encrypted fields with Queryable
81-
Encryption.
82-
83-
To create an encrypted field, inherit from ``EncryptedFieldMixin`` and
84-
your custom field class:
85-
86-
.. code-block:: python
159+
Use this mixin to create encrypted versions of your own custom fields. For
160+
example, to create an encrypted version of ``MyField``::
87161

88162
from django.db import models
89163
from django_mongodb_backend.fields import EncryptedFieldMixin
@@ -93,16 +167,5 @@ MongoDB imposes some restrictions on encrypted fields:
93167
class MyEncryptedField(EncryptedFieldMixin, MyField):
94168
pass
95169

96-
97-
You can then use your custom encrypted field in a model, specifying the
98-
desired query types:
99-
100-
.. code-block:: python
101-
102-
class MyModel(models.Model):
103-
my_encrypted_field = MyEncryptedField(
104-
queries={"queryType": "equality"},
105-
)
106-
my_encrypted_field_too = MyEncryptedField(
107-
queries={"queryType": "range"},
108-
)
170+
This adds the :ref:`queries <encrypted-fields-queries>` argument to the
171+
field.

docs/topics/index.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,5 @@ know:
99
:maxdepth: 2
1010

1111
embedded-models
12-
queryable-encryption
1312
transactions
1413
known-issues

0 commit comments

Comments
 (0)