diff --git a/README.md b/README.md index 1545154d..5ccb2fc5 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,9 @@ explore and build. The best way to share this is via our [MongoDB Community Foru * [Model Your Data](https://www.mongodb.com/docs/languages/python/django-mongodb/current/model-data/) * [Limitations & Future Work](https://www.mongodb.com/docs/languages/python/django-mongodb/current/limitations-upcoming/) +The documentation in the "docs" directory is online at +https://django-mongodb-backend.readthedocs.io/en/latest/. + ## Install Use the version of `django-mongodb-backend` that corresponds to your version of diff --git a/docs/source/conf.py b/docs/source/conf.py index 4a10dc8e..d4651598 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -16,8 +16,8 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.append(str((Path(__file__).parent / "_ext").resolve())) -project = "django_mongodb_backend" -copyright = "2024, The MongoDB Python Team" +project = "Django MongoDB Backend" +copyright = "2025, The MongoDB Python Team" author = "The MongoDB Python Team" release = _version("django_mongodb_backend") @@ -39,12 +39,15 @@ intersphinx_mapping = { "django": ( "https://docs.djangoproject.com/en/5.1/", - "http://docs.djangoproject.com/en/5.1/_objects/", + "https://docs.djangoproject.com/en/5.1/_objects/", ), + "mongodb": ("https://www.mongodb.com/docs/languages/python/django-mongodb/v5.1/", None), "pymongo": ("https://pymongo.readthedocs.io/en/stable/", None), "python": ("https://docs.python.org/3/", None), } +root_doc = "contents" + # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output diff --git a/docs/source/contents.rst b/docs/source/contents.rst new file mode 100644 index 00000000..c044c0e5 --- /dev/null +++ b/docs/source/contents.rst @@ -0,0 +1,23 @@ +================= +Table of contents +================= + +.. toctree:: + :hidden: + + index + +.. toctree:: + :maxdepth: 2 + + intro/index + topics/index + ref/index + faq + internals + +Indices +======= + +* :ref:`genindex` +* :ref:`modindex` diff --git a/docs/source/faq.rst b/docs/source/faq.rst new file mode 100644 index 00000000..34e8f247 --- /dev/null +++ b/docs/source/faq.rst @@ -0,0 +1,34 @@ +=== +FAQ +=== + +This page contains a list of some frequently asked questions. + +Troubleshooting +=============== + +Debug logging +------------- + +To troubleshoot MongoDB connectivity issues, you can enable :doc:`PyMongo's +logging ` using :doc:`Django's LOGGING setting +`. + +This is a minimal :setting:`LOGGING` setting that enables PyMongo's ``DEBUG`` +logging:: + + LOGGING = { + "version": 1, + "disable_existing_loggers": False, + "handlers": { + "console": { + "class": "logging.StreamHandler", + }, + }, + "loggers": { + "pymongo": { + "handlers": ["console"], + "level": "DEBUG", + }, + }, + } diff --git a/docs/source/index.rst b/docs/source/index.rst index 2e2adaf9..46e1bc67 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,19 +1,48 @@ -django-mongodb-backend 5.1.x documentation -========================================== +====================== +Django MongoDB Backend +====================== -.. toctree:: - :maxdepth: 1 - :caption: Contents: +version 5.1.x for Django 5.1.x - fields - querysets - forms - models - embedded-models +.. rubric:: Everything you need to know about Django MongoDB Backend. -Indices and tables -================== +First steps +=========== -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` +**Getting started:** + +- :doc:`Installation ` +- :doc:`Configuration ` + +Getting help +============ + +Having trouble? We’d like to help! + +- Try the :doc:`faq` – it’s got answers to many common questions. + +- Looking for specific information? Try the :ref:`genindex`, :ref:`modindex`, + or the detailed :doc:`table of contents `. + +- Didn't find an answer? You're welcome to ask questions or give feedback on + the `MongoDB Community Forum `_. + +- Report bugs and request features in our :ref:`issue tracker `. + +Models +====== + +**Reference material:** + +- :doc:`ref/models/fields` +- :doc:`ref/models/querysets` +- :doc:`ref/models/models` + +**Topic guides:** + +- :doc:`topics/embedded-models` + +Forms +===== + +- :doc:`ref/forms` diff --git a/docs/source/internals.rst b/docs/source/internals.rst new file mode 100644 index 00000000..6403bcbe --- /dev/null +++ b/docs/source/internals.rst @@ -0,0 +1,25 @@ +================= +Project internals +================= + +Documentation for people working on Django MongoDB Backend itself. This is the +place to go if you'd like to help improve Django MongoDB Backend or learn about +how the project is managed. + +.. _issue-tracker: + +Issue tracker +============= + +To report a bug or to request a new feature in Django MongoDB Backend, please +open an issue in our issue tracker, JIRA: + +1. Create a `JIRA account `_. + +2. Navigate to the `Python Integrations project + `_. + +3. Click **Create Issue**. Please provide as much information as possible about + the issue and the steps to reproduce it. + +Bug reports in JIRA for this project can be viewed by everyone. diff --git a/docs/source/intro/configure.rst b/docs/source/intro/configure.rst new file mode 100644 index 00000000..22f03333 --- /dev/null +++ b/docs/source/intro/configure.rst @@ -0,0 +1,157 @@ +=================================================== +Configuring a project to use Django MongoDB Backend +=================================================== + +Aftering :doc:`installing Django MongoDB Backend `, you must take some +additional steps to configure your project. + +.. _specifying the-default-pk-field: + +Specifying the default primary key field +======================================== + +In your Django settings, you must specify that all models should use +:class:`~django_mongodb_backend.fields.ObjectIdAutoField`. + +You can create a new project that's configured based on these steps using a +project template: + +.. code-block:: bash + + $ django-admin startproject mysite --template https://github.com/mongodb-labs/django-mongodb-project/archive/refs/heads/5.1.x.zip + +(If you're using a version of Django other than 5.1.x, replace the two numbers +to match the first two numbers from your version.) + +This template includes the following line in ``settings.py``:: + + DEFAULT_AUTO_FIELD = "django_mongodb_backend.fields.ObjectIdAutoField" + +But this setting won't override any apps that have an ``AppConfig`` that +specifies :attr:`~django.apps.AppConfig.default_auto_field`. For those apps, +you'll need to create a custom :class:`~django.apps.AppConfig`. + +For example, the project template includes ``/apps.py``:: + + from django.contrib.admin.apps import AdminConfig + from django.contrib.auth.apps import AuthConfig + from django.contrib.contenttypes.apps import ContentTypesConfig + + + class MongoAdminConfig(AdminConfig): + default_auto_field = "django_mongodb_backend.fields.ObjectIdAutoField" + + + class MongoAuthConfig(AuthConfig): + default_auto_field = "django_mongodb_backend.fields.ObjectIdAutoField" + + + class MongoContentTypesConfig(ContentTypesConfig): + default_auto_field = "django_mongodb_backend.fields.ObjectIdAutoField" + +Each app reference in the :setting:`INSTALLED_APPS` setting must point to the +corresponding ``AppConfig``. For example, instead of ``'django.contrib.admin'``, +the template uses ``'.apps.MongoAdminConfig'``. + +Configuring migrations +====================== + +Because all models must use +:class:`~django_mongodb_backend.fields.ObjectIdAutoField`, each third-party +and contrib app you use needs to have its own migrations specific to MongoDB. + +For example, ``settings.py`` in the project template specifies:: + + MIGRATION_MODULES = { + "admin": "mongo_migrations.admin", + "auth": "mongo_migrations.auth", + "contenttypes": "mongo_migrations.contenttypes", + } + +The project template includes these migrations, but you can generate them if +you're setting things up manually or if you need to create migrations for +third-party apps. For example: + +.. code-block:: bash + + $ python manage.py makemigrations admin auth contenttypes + Migrations for 'admin': + mongo_migrations/admin/0001_initial.py + - Create model LogEntry + ... + +Creating Django applications +============================ + +Whenever you run ``python manage.py startapp``, you must remove the line:: + + default_auto_field = 'django.db.models.BigAutoField' + +from the new application's ``apps.py`` file (or change it to reference +``"django_mongodb_backend.fields.ObjectIdAutoField"``). + +Alternatively, you can use the following :djadmin:`startapp` template which +includes this change: + +.. code-block:: bash + + $ python manage.py startapp myapp --template https://github.com/mongodb-labs/django-mongodb-app/archive/refs/heads/5.1.x.zip + +(If you're using a version of Django other than 5.1.x, replace the two numbers +to match the first two numbers from your version.) + +.. _configuring-databases-setting: + +Configuring the ``DATABASES`` setting +===================================== + +After you've set up a project, configure Django's :setting:`DATABASES` setting +similar to this:: + + DATABASES = { + "default": { + "ENGINE": "django_mongodb_backend", + "HOST": "mongodb+srv://cluster0.example.mongodb.net", + "NAME": "my_database", + "USER": "my_user", + "PASSWORD": "my_password", + "PORT": 27017, + "OPTIONS": { + # Example: + "retryWrites": "true", + "w": "majority", + "tls": "false", + }, + }, + } + +For a localhost configuration, you can omit :setting:`HOST` or specify +``"HOST": "localhost"``. + +:setting:`HOST` only needs a scheme prefix for SRV connections +(``mongodb+srv://``). A ``mongodb://`` prefix is never required. + +:setting:`OPTIONS` is an optional dictionary of parameters that will be passed +to :class:`~pymongo.mongo_client.MongoClient`. + +Specify :setting:`USER` and :setting:`PASSWORD` if your database requires +authentication. + +:setting:`PORT` is optional if unchanged from MongoDB's default of 27017. + +For a replica set or sharded cluster where you have multiple hosts, include +all of them in :setting:`HOST`, e.g. +``"mongodb://mongos0.example.com:27017,mongos1.example.com:27017"``. + +Alternatively, if you prefer to simply paste in a MongoDB URI rather than parse +it into the format above, you can use +:func:`~django_mongodb_backend.utils.parse_uri`:: + + import django_mongodb_backend + + MONGODB_URI = "mongodb+srv://my_user:my_password@cluster0.example.mongodb.net/myDatabase?retryWrites=true&w=majority&tls=false" + DATABASES["default"] = django_mongodb_backend.parse_uri(MONGODB_URI) + +This constructs a :setting:`DATABASES` setting equivalent to the first example. + +Congratulations, your project is ready to go! diff --git a/docs/source/intro/index.rst b/docs/source/intro/index.rst new file mode 100644 index 00000000..30bcbbc5 --- /dev/null +++ b/docs/source/intro/index.rst @@ -0,0 +1,26 @@ +=============== +Getting started +=============== + +New to Django or MongoDB? Well, you came to the right place: read this material +to quickly get up and running. + +.. toctree:: + :maxdepth: 1 + + install + configure + +.. seealso:: + + If you're new to Django_, you might want to start by getting an idea of + what it's like. + + For a tutorial that covers using this library with MongoDB Atlas (a fully + managed cloud database service that hosts MongoDB), read + :doc:`mongodb:get-started`. + + Another excellent tutorial that covers Django in more detail is the + :doc:`official Django tutorial `. + + .. _Django: https://www.djangoproject.org/ diff --git a/docs/source/intro/install.rst b/docs/source/intro/install.rst new file mode 100644 index 00000000..b4ddb502 --- /dev/null +++ b/docs/source/intro/install.rst @@ -0,0 +1,17 @@ +================================= +Installing Django MongoDB Backend +================================= + +Use the version of ``django-mongodb-backend`` that corresponds to your version +of Django. For example, to get the latest compatible release for Django 5.1.x: + +.. code-block:: bash + + $ pip install --pre django-mongodb-backend==5.1.* + +(Until the package is out of beta, you must use pip's ``--pre`` option.) + +The minor release number of Django doesn't correspond to the minor release +number of ``django-mongodb-backend``. Use the latest minor release of each. + +Next, you'll have to :doc:`configure your project `. diff --git a/docs/source/forms.rst b/docs/source/ref/forms.rst similarity index 100% rename from docs/source/forms.rst rename to docs/source/ref/forms.rst diff --git a/docs/source/ref/index.rst b/docs/source/ref/index.rst new file mode 100644 index 00000000..08fac924 --- /dev/null +++ b/docs/source/ref/index.rst @@ -0,0 +1,10 @@ +============= +API reference +============= + +.. toctree:: + :maxdepth: 2 + + models/index + forms + utils diff --git a/docs/source/fields.rst b/docs/source/ref/models/fields.rst similarity index 97% rename from docs/source/fields.rst rename to docs/source/ref/models/fields.rst index d8f46297..83bd4848 100644 --- a/docs/source/fields.rst +++ b/docs/source/ref/models/fields.rst @@ -248,7 +248,7 @@ Stores a model of type ``embedded_model``. class Book(models.Model): author = EmbeddedModelField(Author) -See :doc:`embedded-models` for more details and examples. +See :doc:`/topics/embedded-models` for more details and examples. .. admonition:: Migrations support is limited @@ -260,6 +260,14 @@ See :doc:`embedded-models` for more details and examples. created these models and then added an indexed field to ``Address``, the index created in the nested ``Book`` embed is not created. +``ObjectIdAutoField`` +--------------------- + +.. class:: ObjectIdAutoField + +This field is typically the default primary key field for all models stored in +MongoDB. See :ref:`specifying the-default-pk-field`. + ``ObjectIdField`` ----------------- diff --git a/docs/source/ref/models/index.rst b/docs/source/ref/models/index.rst new file mode 100644 index 00000000..d5cb63ce --- /dev/null +++ b/docs/source/ref/models/index.rst @@ -0,0 +1,12 @@ +====== +Models +====== + +Model API reference. + +.. toctree:: + :maxdepth: 1 + + fields + querysets + models diff --git a/docs/source/models.rst b/docs/source/ref/models/models.rst similarity index 81% rename from docs/source/models.rst rename to docs/source/ref/models/models.rst index d0d20054..b85505bd 100644 --- a/docs/source/models.rst +++ b/docs/source/ref/models/models.rst @@ -7,8 +7,8 @@ One MongoDB-specific model is available in ``django_mongodb_backend.models``. .. class:: EmbeddedModel -An abstract model which all :doc:`embedded models ` must -subclass. +An abstract model which all :doc:`embedded models ` +must subclass. Since these models are not stored in their own collection, they do not have any of the normal ``QuerySet`` methods (``all()``, ``filter()``, ``delete()``, diff --git a/docs/source/querysets.rst b/docs/source/ref/models/querysets.rst similarity index 63% rename from docs/source/querysets.rst rename to docs/source/ref/models/querysets.rst index 4c024c77..e8a7afc5 100644 --- a/docs/source/querysets.rst +++ b/docs/source/ref/models/querysets.rst @@ -1,6 +1,43 @@ +========================== ``QuerySet`` API reference ========================== +Supported ``QuerySet`` methods +============================== + +All of Django's :doc:`QuerySet methods ` are +supported, except: + + - :meth:`bulk_update() ` + - :meth:`dates() ` + - :meth:`datetimes() ` + - :meth:`distinct() ` + - :meth:`extra() ` + - :meth:`prefetch_related() ` + +In addition, :meth:`QuerySet.delete() ` +and :meth:`update() ` do not support +queries that span multiple collections. + +``QuerySet.explain()`` +====================== + +- :meth:`QuerySet.explain() ` supports + the `comment and verbosity options + `_. + + Example:: + + Model.objects.explain(comment="...", verbosity="...") + + Valid values for ``verbosity`` are ``"queryPlanner"`` (default), + ``"executionStats"``, and ``"allPlansExecution"``. + +MongoDB-specific ``QuerySet`` methods +===================================== + +.. class:: django_mongodb_backend.managers.MongoManager + Some MongoDB-specific ``QuerySet`` methods are available by adding a custom :class:`~django.db.models.Manager`, ``MongoManager``, to your model:: diff --git a/docs/source/ref/utils.rst b/docs/source/ref/utils.rst new file mode 100644 index 00000000..16a61165 --- /dev/null +++ b/docs/source/ref/utils.rst @@ -0,0 +1,38 @@ +=================== +Utils API reference +=================== + +.. module:: django_mongodb_backend.utils + :synopsis: Built-in utilities. + +This document covers the public API parts of ``django_mongodb_backend.utils``. +Most of the module's contents are designed for internal use and only the +following parts can be considered stable. + +``parse_uri()`` +=============== + +.. function:: parse_uri(uri, conn_max_age=0, test=None) + +Parses a MongoDB `connection string`_ into a dictionary suitable for Django's +:setting:`DATABASES` setting. + +.. _connection string: https://www.mongodb.com/docs/manual/reference/connection-string/ + +Example:: + + import django_mongodb_backend + + MONGODB_URI = "mongodb+srv://my_user:my_password@cluster0.example.mongodb.net/myDatabase?retryWrites=true&w=majority&tls=false" + DATABASES["default"] = django_mongodb_backend.parse_uri(MONGODB_URI) + +You can use the parameters to customize the resulting :setting:`DATABASES` +setting: + +- Use ``conn_max_age`` to configure :ref:`persistent database connections + `. +- Use ``test`` to provide a dictionary of settings for test databases in the + format of :setting:`TEST `. + +But for maximum flexibility, construct :setting:`DATABASES` manually as +described in :ref:`configuring-databases-setting`. diff --git a/docs/source/embedded-models.rst b/docs/source/topics/embedded-models.rst similarity index 100% rename from docs/source/embedded-models.rst rename to docs/source/topics/embedded-models.rst diff --git a/docs/source/topics/index.rst b/docs/source/topics/index.rst new file mode 100644 index 00000000..63ff9a25 --- /dev/null +++ b/docs/source/topics/index.rst @@ -0,0 +1,12 @@ +============================ +Using Django MongoDB Backend +============================ + +Introductions to some of the key parts of Django MongoDB Backend you'll need to +know: + +.. toctree:: + :maxdepth: 2 + + embedded-models + known-issues diff --git a/docs/source/topics/known-issues.rst b/docs/source/topics/known-issues.rst new file mode 100644 index 00000000..00572786 --- /dev/null +++ b/docs/source/topics/known-issues.rst @@ -0,0 +1,102 @@ +============================ +Known issues and limitations +============================ + +This document summarizes some known issues and limitations of this library. +If you notice an issue not listed, use the :ref:`issue-tracker` to report a bug +or request a feature. + +Like any database, MongoDB has some particularities. Also keep in mind that +because MongoDB is a NoSQL database, it's impossible to implement SQL-specific +functionality. + +Model fields +============ + +- :class:`~django.db.models.DateTimeField` is limited to millisecond precision + (rather than microsecond like most other databases), and correspondingly, + :class:`~django.db.models.DurationField` stores milliseconds rather than + microseconds. + +- Some of Django's built-in fields aren't supported by MongoDB: + + - :class:`~django.db.models.AutoField` (including + :class:`~django.db.models.BigAutoField` and + :class:`~django.db.models.SmallAutoField`) + - :class:`~django.db.models.GeneratedField` + +Querying +======== + +- The following ``QuerySet`` methods aren't supported: + + - :meth:`bulk_update() ` + - :meth:`dates() ` + - :meth:`datetimes() ` + - :meth:`distinct() ` + - :meth:`extra() ` + - :meth:`prefetch_related() ` + +- :meth:`QuerySet.delete() ` and + :meth:`update() ` do not support queries + that span multiple collections. + +- When querying :class:`~django.db.models.JSONField`: + + - There is no way to distinguish between a JSON ``"null"`` (represented by + ``Value(None, JSONField())``) and a SQL ``null`` (queried using the + :lookup:`isnull` lookup). Both of these queries return both of these nulls. + - Some queries with ``Q`` objects, e.g. ``Q(value__foo="bar")``, don't work + properly, particularly with ``QuerySet.exclude()``. + - Filtering for a ``None`` key, e.g. ``QuerySet.filter(value__j=None)`` + incorrectly returns objects where the key doesn't exist. + - You can study the skipped tests in ``DatabaseFeatures.django_test_skips`` + for more details on known issues. + +Database functions +================== + +- Some of Django's built-in database functions aren't supported by MongoDB: + + - :class:`~django.db.models.functions.Chr` + - :class:`~django.db.models.functions.ExtractQuarter` + - :class:`~django.db.models.functions.LPad`, + :class:`~django.db.models.functions.RPad` + - :class:`~django.db.models.functions.MD5` + - :class:`~django.db.models.functions.Now` + - :class:`~django.db.models.functions.Ord` + - :class:`~django.db.models.functions.Repeat` + - :class:`~django.db.models.functions.Reverse` + - :class:`~django.db.models.functions.Right` + - :class:`~django.db.models.functions.SHA1`, + :class:`~django.db.models.functions.SHA224`, + :class:`~django.db.models.functions.SHA256`, + :class:`~django.db.models.functions.SHA384`, + :class:`~django.db.models.functions.SHA512` + - :class:`~django.db.models.functions.Sign` + +- The ``tzinfo`` parameter of the :class:`~django.db.models.functions.Trunc` + database functions doesn't work properly because MongoDB converts the result + back to UTC. + +Transaction management +====================== + +Query execution uses Django and MongoDB's default behavior of autocommit mode. +Each query is immediately committed to the database. + +Django's :doc:`transaction management APIs ` +are not supported. + +Database introspection +====================== + +Due to the lack of ability to introspect MongoDB collection schema, +:djadmin:`inspectdb` and :option:`migrate --fake-initial` aren't supported. + +Caching +======= + +:ref:`Database caching ` is not supported since the built-in +database cache backend requires SQL. A custom cache backend for MongoDB will be +provided in the future. diff --git a/pyproject.toml b/pyproject.toml index 5f1a098e..78ba83d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -120,3 +120,6 @@ partial_branches = ["if (.*and +)*not _use_c( and.*)*:"] [tool.coverage.html] directory = "htmlcov" + +[tool.rstcheck] +report_level = "WARNING"