-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Implements a MongoDB backend for Django, which supports CRUD operations, relations, single-table inheritance and single-table OneToOne relations. Not implemented yet are joins, aggregations (other than count), and embedded document arrays. * chore: ruff + black + pre-commit * fix: bug in row item lookup * fix: getter for null values * fix: get prep value * chore: guard rhs sub-query expressions / fix aggregation result * fix: use proper pk field if order by pk * chore: reset migrations, mongodb as secondary, better distinct aggreation * fix: post-filter search results + more docs * chore: search improvements * chore: search improvements * chore: update version tag * chore: github actions * Change version to 0.19 * Change version to 0.20 * chore: search query bug * chore: fmt * Change version to 0.21 * chore: tests for mongodb search * fix: wrong init file name * fix: wrong init file name * fix: unused fixture * fix: implements NothingNode * Change version to 0.22 * chore: refactoring query nodes for reuse * chore: db prep * fix: operator rename * chore: publish * chore: publish (fix) * Change version to 0.19 * chore: small cleanup * chore: skip empty rhs search filters * chore: add support for autocomplete search * chore: add shorthand to retrieve collection from cursor * feat: implements raw mongo query * feat: search fixes + test update * chore: update pyproject.toml + django 5.0 support --------- Co-authored-by: PyPI Poetry Publish Bot <admin@code-specialist.com>
- Loading branch information
Showing
50 changed files
with
2,998 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
name: Test | ||
|
||
on: | ||
push: | ||
branches: | ||
- '*' | ||
|
||
jobs: | ||
test: | ||
name: Test | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
|
||
- name: Setup Python | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: '3.11' | ||
|
||
- name: Setup poetry cache | ||
uses: actions/cache@v2 | ||
with: | ||
path: ~/.cache/pypoetry | ||
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }} | ||
restore-keys: | | ||
${{ runner.os }}-poetry- | ||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install poetry | ||
poetry config virtualenvs.create false | ||
poetry install | ||
- name: Run linters | ||
run: | | ||
poetry run ruff . | ||
poetry run black --check . | ||
- name: Start MongoDB | ||
uses: supercharge/mongodb-github-action@1.10.0 | ||
with: | ||
mongodb-version: 6.0 | ||
|
||
- name: Run tests | ||
env: | ||
MONGODB_URL: mongodb://localhost:27017 | ||
CI: true | ||
run: | | ||
poetry run pytest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
name: Test and release on tag | ||
|
||
on: | ||
release: | ||
types: [ published ] | ||
|
||
jobs: | ||
publish-service-client-package: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: write | ||
steps: | ||
- name: Publish PyPi package | ||
uses: code-specialist/pypi-poetry-publish@v1 | ||
with: | ||
ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
PUBLISH_REGISTRY_PASSWORD: ${{ secrets.PYPI_TOKEN }} | ||
BRANCH: main | ||
PACKAGE_DIRECTORY: ./django_mongodb/ | ||
PYTHON_VERSION: 3.11 | ||
POETRY_VERSION: 1.7.1 | ||
POETRY_CORE_VERSION: 1.8.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Mac | ||
.DS_Store | ||
|
||
# Windows | ||
Thumbs.db | ||
|
||
# Editor | ||
.idea/ | ||
.vscode | ||
|
||
# vi | ||
*~ | ||
|
||
# General | ||
log/ | ||
*.log | ||
|
||
# Django | ||
/db.sqlite3 | ||
/.mypy_cache/ | ||
.env | ||
/public/ | ||
__pycache__ | ||
|
||
oidc.key | ||
/.ruff_cache/ | ||
/copilot/ | ||
/.coverage | ||
|
||
/media/mail_media/* | ||
|
||
/track/data | ||
|
||
/dist/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
repos: | ||
- repo: https://github.com/pre-commit/pre-commit-hooks | ||
rev: v3.2.0 | ||
hooks: | ||
- id: trailing-whitespace | ||
- id: end-of-file-fixer | ||
- id: check-yaml | ||
- id: check-added-large-files | ||
|
||
- repo: https://github.com/charliermarsh/ruff-pre-commit | ||
# Ruff version. | ||
rev: "v0.3.5" | ||
hooks: | ||
- id: ruff | ||
args: [--fix, --exit-non-zero-on-fix] | ||
- id: ruff-format |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
setup_local_db: | ||
@echo "Creating local database..." | ||
@atlas deployments setup local --type local --port 3307 --force | ||
@echo "Local database created." | ||
|
||
start_local_db: | ||
@echo "Starting local database..." | ||
@atlas deployments start local | ||
@echo "Local database started." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
## Django backend for MongoDB | ||
|
||
Django backend for MongoDB. | ||
|
||
Supports: | ||
- Column mappings to MongoDB documents | ||
- Single table (collection) inheritance and single table OneToOne relationships | ||
- Filters (filter/exclude) | ||
|
||
## Setup / Configuration | ||
|
||
Not supported as primary database, as Django contrib apps rely on Integer primary keys in built in migrations (and | ||
because it is a use case that is not a priority at the moment). | ||
|
||
```python | ||
# settings.py | ||
DATABASES = { | ||
# or any other primary databse | ||
"default": { | ||
"ENGINE": "django.db.backends.sqlite3", | ||
"NAME": BASE_DIR / "db.sqlite3", | ||
}, | ||
# mongodb database, Client constructor options are passe in 'CLIENT', the database name in 'NAME' | ||
"mongodb": { | ||
"ENGINE": "django_mongodb", | ||
"NAME": "django_mongodb", | ||
"CONN_MAX_AGE": 120, | ||
"CLIENT": { | ||
"host": os.environ.get("MONGODB_URL"), | ||
}, | ||
}, | ||
} | ||
# A database is required | ||
DATABASE_ROUTERS = ["testproject.router.DatabaseRouter"] | ||
``` | ||
|
||
Using the database in models requires a DatabaseRouter, which could look like this | ||
```python | ||
class DatabaseRouter: | ||
def db_for_read(self, model, **hints): | ||
if model._meta.app_label == "mymongoapp": | ||
return "default" | ||
return "default" | ||
|
||
def db_for_write(self, model, **hints): | ||
if model._meta.app_label == "mymongoapp": | ||
return "default" | ||
return "default" | ||
|
||
def allow_relation(self, obj1, obj2, **hints): | ||
if obj1._meta.app_label == obj2._meta.app_label: | ||
return True | ||
return None | ||
|
||
def allow_migrate(self, db, app_label, model_name=None, **hints): | ||
if app_label == "mymongoapp": | ||
# we are disabling migrations, as MongoDB is schema-less. Alerts, such as renaming fields, etc. are not supported | ||
return False | ||
return None | ||
``` | ||
|
||
Finally we are going to change the default primary key of the app using MongoDB (if that is the case, otherwise add | ||
ObjectIdAutoField to the models, where you need it). | ||
|
||
```python | ||
# apps.py | ||
class TestappConfig(AppConfig): | ||
default_auto_field = "django_mongodb.models.ObjectIdAutoField" | ||
name = "mymongoapp" | ||
``` | ||
|
||
### Defining Models | ||
A simple model, in an app, which has `ObjectIdAutoField` as `default_auto_field` | ||
|
||
```python | ||
class MyModel(models.Model): | ||
json_field = JSONField() | ||
name = models.CharField(max_length=100) | ||
datetime_field = models.DateTimeField(auto_now_add=True) | ||
time_field = models.TimeField(auto_now_add=True) | ||
date_field = models.DateField(auto_now_add=True) | ||
``` | ||
|
||
Single table inheritance | ||
|
||
```python | ||
class SameTableChild(MyModel): | ||
my_model_ptr = models.OneToOneField( | ||
MyModel, | ||
on_delete=models.CASCADE, | ||
parent_link=True, | ||
related_name="same_table_child", | ||
# pointer to the primary key of the parent model | ||
db_column="_id", | ||
) | ||
extended = models.CharField(max_length=100) | ||
|
||
class Meta: | ||
# We are using the parent collection as db_table | ||
db_table = "mymongoapp_mymodel" | ||
``` | ||
|
||
Single table `OneToOne` relationships | ||
|
||
```python | ||
class SameTableOneToOne(models.Model): | ||
dummy_model = models.OneToOneField( | ||
MyModel, | ||
primary_key=True, | ||
on_delete=models.CASCADE, | ||
related_name="extends", | ||
db_column="_id", | ||
) | ||
extra = models.CharField(max_length=100) | ||
|
||
class Meta: | ||
# we are using the same collection to persist one-to-one relationships | ||
db_table = "mymongoapp_mymodel" | ||
``` | ||
|
||
### Querying | ||
|
||
```python | ||
# get all objects | ||
MyModel.objects.all() | ||
|
||
# get all objects, which have a name in list ["foo", "bar"] | ||
MyModel.objects.filter(name_in=["foo", "bar"]) | ||
|
||
# select related with single table inheritance and one to one relationships | ||
MyModel.objects.select_related("same_table_child", "extends").all() | ||
|
||
# simple aggregations | ||
MyModel.objects.filter(name_in=["foo", "bar"]).count() | ||
``` | ||
|
||
### Search | ||
Using the `prefer_search()` extension of MongoQueryset, we can use the `$search` operator of MongoDB to query, | ||
if we have search indexes configured on the model. | ||
|
||
```python | ||
MyModel.objects.prefer_search().filter(name="foo").all() | ||
``` | ||
|
||
PostgreSQL search vectors map down to MongoDB search indexes, so we can use the same syntax as with PostgreSQL. | ||
|
||
```python | ||
class MyModel(models.Model): | ||
name = models.CharField(max_length=100) | ||
``` | ||
|
||
```python | ||
MyModel.objects.annotate(search=SearchVector('name')).filter(search=SearchQuery('foo')).all() | ||
``` | ||
|
||
### Raw Queries | ||
|
||
```python | ||
with connections["mongodb"].cursor() as cursor: | ||
doc = cursor.collections["my_collection"].find_one() | ||
assert isinstance(doc["_id"], ObjectId) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__version__ = "0.22" |
Oops, something went wrong.