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 21, 2024
1 parent 52de76f commit c53de90
Show file tree
Hide file tree
Showing 20 changed files with 1,162 additions and 81 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/mongodb_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
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
51 changes: 32 additions & 19 deletions .github/workflows/test-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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/[email protected]
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 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.

133 changes: 133 additions & 0 deletions django_mongodb/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import copy

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:
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'
SchemaEditorClass = DatabaseSchemaEditor
Database = Database

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 = copy.deepcopy(self.settings_dict)

def pop(name, default=None):
return settings.pop(name) or default

db_name = pop('NAME')
host = pop('HOST')
port = pop('PORT', 27017)
user = pop('USER')
password = pop('PASSWORD')
options = pop('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': host,
'port': int(port),
'document_class': dict,
'tz_aware': False,
}
conn_options.update(options)

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

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 c53de90

Please sign in to comment.