Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

INTPYTHON-509 Add db_name parameter to parse_uri() #249

Merged
merged 1 commit into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/mongodb_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django_mongodb_backend import parse_uri

if mongodb_uri := os.getenv("MONGODB_URI"):
db_settings = parse_uri(mongodb_uri)
db_settings = parse_uri(mongodb_uri, db_name="dummy")

# Workaround for https://github.com/mongodb-labs/mongo-orchestration/issues/268
if db_settings["USER"] and db_settings["PASSWORD"]:
Expand Down
17 changes: 5 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,24 +46,17 @@ $ django-admin startproject example --template https://github.com/mongodb-labs/d

### Connect to the database

Navigate to your `example/settings.py` file and find the variable named
`DATABASES` Replace the `DATABASES` variable with this:
Navigate to your `example/settings.py` file and replace the `DATABASES`
setting like so:

```python
DATABASES = {
"default": django_mongodb_backend.parse_uri("<CONNECTION_STRING_URI>"),
"default": django_mongodb_backend.parse_uri(
"<CONNECTION_STRING_URI>", db_name="example"
),
}
```

The MongoDB `<CONNECTION_STRING_URI>` must also specify a database for the
`parse_uri` function to work.
If not already included, make sure you provide a value for `<DATABASE_NAME>`
in your URI as shown in the example below:
```bash
mongodb+srv://myDatabaseUser:D1fficultP%[email protected]/<DATABASE_NAME>?retryWrites=true&w=majority
```


### Run the server
To verify that you installed Django MongoDB Backend and correctly configured your project, run the following command from your project root:
```bash
Expand Down
9 changes: 7 additions & 2 deletions django_mongodb_backend/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def check_django_compatability():
)


def parse_uri(uri, conn_max_age=0, test=None):
def parse_uri(uri, *, db_name=None, conn_max_age=0, test=None):
"""
Convert the given uri into a dictionary suitable for Django's DATABASES
setting.
Expand All @@ -45,16 +45,21 @@ def parse_uri(uri, conn_max_age=0, test=None):
host, port = nodelist[0]
elif len(nodelist) > 1:
host = ",".join([f"{host}:{port}" for host, port in nodelist])
db_name = db_name or uri["database"]
if not db_name:
raise ImproperlyConfigured("You must provide the db_name parameter.")
settings_dict = {
"ENGINE": "django_mongodb_backend",
"NAME": uri["database"],
"NAME": db_name,
"HOST": host,
"PORT": port,
"USER": uri.get("username"),
"PASSWORD": uri.get("password"),
"OPTIONS": uri.get("options"),
"CONN_MAX_AGE": conn_max_age,
}
if "authSource" not in settings_dict["OPTIONS"] and uri["database"]:
settings_dict["OPTIONS"]["authSource"] = uri["database"]
if test:
settings_dict["TEST"] = test
return settings_dict
Expand Down
9 changes: 6 additions & 3 deletions docs/source/ref/utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ following parts can be considered stable.
``parse_uri()``
===============

.. function:: parse_uri(uri, conn_max_age=0, test=None)
.. function:: parse_uri(uri, db_name=None, conn_max_age=0, test=None)

Parses a MongoDB `connection string`_ into a dictionary suitable for Django's
:setting:`DATABASES` setting.
Expand All @@ -23,8 +23,11 @@ Example::

import django_mongodb_backend

MONGODB_URI = "mongodb+srv://my_user:[email protected]/myDatabase?retryWrites=true&w=majority&tls=false"
DATABASES["default"] = django_mongodb_backend.parse_uri(MONGODB_URI)
MONGODB_URI = "mongodb+srv://my_user:[email protected]/defaultauthdb?retryWrites=true&w=majority&tls=false"
DATABASES["default"] = django_mongodb_backend.parse_uri(MONGODB_URI, db_name="example")

You must specify ``db_name`` (the :setting:`NAME` of your database) if the URI
doesn't specify ``defaultauthdb``.

You can use the parameters to customize the resulting :setting:`DATABASES`
setting:
Expand Down
49 changes: 39 additions & 10 deletions tests/backend_/utils/test_parse_uri.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from unittest.mock import patch

import pymongo
from django.core.exceptions import ImproperlyConfigured
from django.test import SimpleTestCase

from django_mongodb_backend import parse_uri
Expand All @@ -12,11 +13,28 @@ def test_simple_uri(self):
self.assertEqual(settings_dict["ENGINE"], "django_mongodb_backend")
self.assertEqual(settings_dict["NAME"], "myDatabase")
self.assertEqual(settings_dict["HOST"], "cluster0.example.mongodb.net")
self.assertEqual(settings_dict["OPTIONS"], {"authSource": "myDatabase"})

def test_no_database(self):
settings_dict = parse_uri("mongodb://cluster0.example.mongodb.net")
self.assertIsNone(settings_dict["NAME"])
def test_db_name(self):
settings_dict = parse_uri("mongodb://cluster0.example.mongodb.net/", db_name="myDatabase")
self.assertEqual(settings_dict["ENGINE"], "django_mongodb_backend")
self.assertEqual(settings_dict["NAME"], "myDatabase")
self.assertEqual(settings_dict["HOST"], "cluster0.example.mongodb.net")
self.assertEqual(settings_dict["OPTIONS"], {})

def test_db_name_overrides_default_auth_db(self):
settings_dict = parse_uri(
"mongodb://cluster0.example.mongodb.net/default_auth_db", db_name="myDatabase"
)
self.assertEqual(settings_dict["ENGINE"], "django_mongodb_backend")
self.assertEqual(settings_dict["NAME"], "myDatabase")
self.assertEqual(settings_dict["HOST"], "cluster0.example.mongodb.net")
self.assertEqual(settings_dict["OPTIONS"], {"authSource": "default_auth_db"})

def test_no_database(self):
msg = "You must provide the db_name parameter."
with self.assertRaisesMessage(ImproperlyConfigured, msg):
parse_uri("mongodb://cluster0.example.mongodb.net")

def test_srv_uri_with_options(self):
uri = "mongodb+srv://my_user:[email protected]/my_database?retryWrites=true&w=majority"
Expand All @@ -30,35 +48,46 @@ def test_srv_uri_with_options(self):
self.assertEqual(settings_dict["PASSWORD"], "my_password")
self.assertIsNone(settings_dict["PORT"])
self.assertEqual(
settings_dict["OPTIONS"], {"retryWrites": True, "w": "majority", "tls": True}
settings_dict["OPTIONS"],
{"authSource": "my_database", "retryWrites": True, "w": "majority", "tls": True},
)

def test_localhost(self):
settings_dict = parse_uri("mongodb://localhost")
settings_dict = parse_uri("mongodb://localhost/db")
self.assertEqual(settings_dict["HOST"], "localhost")
self.assertEqual(settings_dict["PORT"], 27017)

def test_localhost_with_port(self):
settings_dict = parse_uri("mongodb://localhost:27018")
settings_dict = parse_uri("mongodb://localhost:27018/db")
self.assertEqual(settings_dict["HOST"], "localhost")
self.assertEqual(settings_dict["PORT"], 27018)

def test_hosts_with_ports(self):
settings_dict = parse_uri("mongodb://localhost:27017,localhost:27018")
settings_dict = parse_uri("mongodb://localhost:27017,localhost:27018/db")
self.assertEqual(settings_dict["HOST"], "localhost:27017,localhost:27018")
self.assertEqual(settings_dict["PORT"], None)

def test_hosts_without_ports(self):
settings_dict = parse_uri("mongodb://host1.net,host2.net")
settings_dict = parse_uri("mongodb://host1.net,host2.net/db")
self.assertEqual(settings_dict["HOST"], "host1.net:27017,host2.net:27017")
self.assertEqual(settings_dict["PORT"], None)

def test_auth_source_in_query_string(self):
settings_dict = parse_uri("mongodb://localhost/?authSource=auth", db_name="db")
self.assertEqual(settings_dict["NAME"], "db")
self.assertEqual(settings_dict["OPTIONS"], {"authSource": "auth"})

def test_auth_source_in_query_string_overrides_defaultauthdb(self):
settings_dict = parse_uri("mongodb://localhost/db?authSource=auth")
self.assertEqual(settings_dict["NAME"], "db")
self.assertEqual(settings_dict["OPTIONS"], {"authSource": "auth"})

def test_conn_max_age(self):
settings_dict = parse_uri("mongodb://localhost", conn_max_age=600)
settings_dict = parse_uri("mongodb://localhost/db", conn_max_age=600)
self.assertEqual(settings_dict["CONN_MAX_AGE"], 600)

def test_test_kwarg(self):
settings_dict = parse_uri("mongodb://localhost", test={"NAME": "test_db"})
settings_dict = parse_uri("mongodb://localhost/db", test={"NAME": "test_db"})
self.assertEqual(settings_dict["TEST"], {"NAME": "test_db"})

def test_invalid_credentials(self):
Expand Down
Loading