Skip to content

Commit

Permalink
initial commit of MongoDB backend
Browse files Browse the repository at this point in the history
  • Loading branch information
timgraham committed Apr 22, 2024
1 parent 52de76f commit 36a9098
Show file tree
Hide file tree
Showing 20 changed files with 1,131 additions and 83 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/mongodb_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
DATABASES = {
"default": {
"ENGINE": "django_mongodb",
"NAME": "test-default",
},
"other": {
"ENGINE": "django_mongodb",
"NAME": "test-other",
},
}
DEFAULT_AUTO_FIELD = "django_mongodb.fields.MongoAutoField"
PASSWORD_HASHERS = ("django.contrib.auth.hashers.MD5PasswordHasher",)
SECRET_KEY = "django_tests_secret_key"
USE_TZ = False
56 changes: 34 additions & 22 deletions .github/workflows/test-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.8
python-version: '3.10'
cache: 'pip'
cache-dependency-path: 'pyproject.toml'
- name: Install Python dependencies
Expand All @@ -32,33 +32,46 @@ jobs:
pre-commit run --hook-stage=manual --all-files
build:
# supercharge/mongodb-github-action requires containers so we don't test other platforms
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04]
python-version: ["3.8", "3.11", "pypy-3.9"]
fail-fast: false
name: CPython ${{ matrix.python-version }}-${{ matrix.os }}
name: Django Test Suite
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
- name: Checkout django-mongodb
uses: actions/checkout@v4
- name: install the django-mongodb backend
run: |
pip3 install --upgrade pip
pip3 install -e .
- name: Checkout Django
uses: actions/checkout@v4
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: 'pyproject.toml'
- name: Install dependencies
repository: 'timgraham/django'
ref: 'mongodb-5.0.x'
path: 'django_repo'
- name: Install system packages for Django's Python test dependencies
run: |
pip install -U pip
pip install -e ".[test]"
sudo apt-get update
sudo apt-get install libmemcached-dev
- name: Install Django and its Python test dependencies
run: |
cd django_repo/tests/
pip3 install -e ..
pip3 install -r requirements/py3.txt
- name: Copy the test settings file
run: cp .github/workflows/mongodb_settings.py django_repo/tests/
- name: Start MongoDB
uses: supercharge/mongodb-github-action@1.10.0
with:
mongodb-version: 4.4
- name: Run tests
run: |
pytest
run: >
python3 django_repo/tests/runtests.py --settings mongodb_settings -v 2
basic
from_db_value
model_fields.test_datetimefield
model_fields.test_decimalfield
model_fields.test_charfield
model_fields.test_textfield
or_lookups
docs:
name: Docs Checks
Expand All @@ -69,8 +82,7 @@ jobs:
with:
cache: 'pip'
cache-dependency-path: 'pyproject.toml'
# Build docs on lowest supported Python for furo
python-version: '3.8'
python-version: '3.10'
- name: Install dependencies
run: |
pip install -U pip
Expand Down
11 changes: 1 addition & 10 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,8 @@ repos:
args: ["--schemafile", "https://json.schemastore.org/github-workflow"]
stages: [manual]

- repo: https://github.com/ariebovenberg/slotscheck
rev: v0.17.0
hooks:
- id: slotscheck
files: \.py$
exclude: "^(test|docs)/"
stages: [manual]
args: ["--no-strict-imports"]

- repo: https://github.com/codespell-project/codespell
rev: "v2.2.6"
hooks:
- id: codespell
args: ["-L", ""]
args: ["-L", "nin"]
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This library is in the early stages of development, and so it's possible the API
Use the version of `django-mongodb` that corresponds to your version of
Django. For example, to get the latest compatible release for Django 5.0.x:

`pip install django-mongodb==0.1.*`
`pip install django-mongodb==5.0.*`

The minor release number of Django doesn't correspond to the minor release
number of django-mongodb. Use the latest minor release of each.
Expand All @@ -19,11 +19,8 @@ DATABASES = {
"default": {
"ENGINE": "django_mongodb",
"NAME": "MY_DATABASE",
"SCHEMA": "MY_SCHEMA",
"WAREHOUSE": "MY_WAREHOUSE",
"USER": "my_user",
"PASSWORD": "my_password",
"ACCOUNT": "my_account",
},
}
```
Expand All @@ -34,6 +31,10 @@ TODO

## Troubleshooting

### Debug logging

TODO

## Credits

This project began by borrowing code from Django non-rel's
[MongoDB Engine](https://github.com/django-nonrel/mongodb-engine),
abandoned since 2015 and Django 1.6.
7 changes: 7 additions & 0 deletions django_mongodb/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
__version__ = "5.0a0"

# Check Django compatibility before other imports which may fail if the
# wrong version of Django is installed.
from .utils import check_django_compatability

check_django_compatability()
3 changes: 0 additions & 3 deletions django_mongodb/_version.py

This file was deleted.

126 changes: 126 additions & 0 deletions django_mongodb/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from django.core.exceptions import ImproperlyConfigured
from django.db.backends.base.base import BaseDatabaseWrapper
from django.db.backends.signals import connection_created
from pymongo.collection import Collection
from pymongo.mongo_client import MongoClient

from . import dbapi as Database
from .client import DatabaseClient
from .creation import DatabaseCreation
from .features import DatabaseFeatures
from .introspection import DatabaseIntrospection
from .operations import DatabaseOperations
from .schema import DatabaseSchemaEditor


class Cursor:
"""A "nodb" cursor that does nothing except work on a context manager."""

def __enter__(self):
pass

def __exit__(self, exception_type, exception_value, exception_traceback):
pass


class DatabaseWrapper(BaseDatabaseWrapper):
data_types = {
"AutoField": "int",
"BigAutoField": "long",
"BinaryField": "binData",
"BooleanField": "bool",
"CharField": "string",
"DateField": "date",
"DateTimeField": "date",
"DecimalField": "decimal",
"DurationField": "long",
"FileField": "string",
"FilePathField": "string",
"FloatField": "double",
"IntegerField": "int",
"BigIntegerField": "long",
"GenericIPAddressField": "string",
"NullBooleanField": "bool",
"OneToOneField": "int",
"PositiveIntegerField": "long",
"PositiveSmallIntegerField": "int",
"SlugField": "string",
"SmallIntegerField": "int",
"TextField": "string",
"TimeField": "date",
"UUIDField": "string",
}

vendor = "mongodb"
Database = Database
SchemaEditorClass = DatabaseSchemaEditor
client_class = DatabaseClient
creation_class = DatabaseCreation
features_class = DatabaseFeatures
introspection_class = DatabaseIntrospection
ops_class = DatabaseOperations

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.connected = False
del self.connection

def get_collection(self, name, **kwargs):
return Collection(self.database, name, **kwargs)

def __getattr__(self, attr):
if attr in ["connection", "database"]:
assert not self.connected
self._connect()
return getattr(self, attr)
raise AttributeError(attr)

def _connect(self):
settings_dict = self.settings_dict

options = settings_dict["OPTIONS"]
self.operation_flags = options.pop("OPERATIONS", {})
if not any(k in ["save", "delete", "update"] for k in self.operation_flags):
# Flags apply to all operations.
flags = self.operation_flags
self.operation_flags = {"save": flags, "delete": flags, "update": flags}

conn_options = {
"host": settings_dict["HOST"] or None,
"port": int(settings_dict["PORT"] or 27017),
"document_class": dict,
"tz_aware": False,
}
conn_options.update(options)

self.connection = MongoClient(**conn_options)
db_name = settings_dict["NAME"]
if db_name:
self.database = self.connection[db_name]

user = settings_dict["USER"]
password = settings_dict["PASSWORD"]
if user and password and not self.database.authenticate(user, password):
raise ImproperlyConfigured("Invalid username or password.")

self.connected = True
connection_created.send(sender=self.__class__, connection=self)

def _reconnect(self):
if self.connected:
del self.connection
del self.database
self.connected = False
self._connect()

def _commit(self):
pass

def _rollback(self):
pass

def close(self):
pass

def cursor(self):
return Cursor()
21 changes: 21 additions & 0 deletions django_mongodb/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import signal

from django.db.backends.base.client import BaseDatabaseClient


class DatabaseClient(BaseDatabaseClient):
executable_name = "mongo"

@classmethod
def settings_to_cmd_args_env(cls, settings_dict, parameters):
raise NotImplementedError

def runshell(self, parameters):
sigint_handler = signal.getsignal(signal.SIGINT)
try:
# Allow SIGINT to pass to mongo to abort queries.
signal.signal(signal.SIGINT, signal.SIG_IGN)
super().runshell(parameters)
finally:
# Restore the original SIGINT handler.
signal.signal(signal.SIGINT, sigint_handler)
Loading

0 comments on commit 36a9098

Please sign in to comment.