├── .flake8
├── .github
└── workflows
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .versionrc.json
├── AUTHORS
├── CHANGELOG.md
├── LICENSE
├── MANIFEST.in
├── Makefile
├── OSSMETADATA
├── README.rst
├── codegen.py
├── django_mongoengine
├── __init__.py
├── apps.py
├── contrib
│ ├── __init__.py
│ └── drf_spectacular.py
├── document.py
├── fields
│ ├── __init__.py
│ ├── djangoflavor.py
│ └── internal.py
├── forms
│ ├── __init__.py
│ ├── document_options.py
│ ├── documents.py
│ ├── fields.py
│ ├── utils.py
│ └── widgets.py
├── mongo_admin
│ ├── __init__.py
│ ├── actions.py
│ ├── apps.py
│ ├── decorators.py
│ ├── helpers.py
│ ├── management
│ │ ├── __init__.py
│ │ └── commands
│ │ │ ├── __init__.py
│ │ │ └── createmongodbsuperuser.py
│ ├── options.py
│ ├── sites.py
│ ├── static
│ │ ├── dict.js
│ │ ├── helper.js
│ │ └── jquery-1.8.0.min.js
│ ├── templates
│ │ └── mongo_admin
│ │ │ └── index.html
│ ├── util.py
│ ├── validation.py
│ └── views.py
├── mongo_auth
│ ├── __init__.py
│ ├── admin.py
│ ├── backends.py
│ ├── managers.py
│ └── models.py
├── paginator.py
├── queryset.py
├── sessions.py
├── utils
│ ├── __init__.py
│ ├── monkey.py
│ └── wrappers.py
└── views
│ ├── __init__.py
│ ├── detail.py
│ ├── edit.py
│ ├── embedded.py
│ ├── list.py
│ └── templates
│ └── _forms.html
├── docs
├── Makefile
├── _static
│ └── debugtoolbar.png
├── _themes
│ ├── README
│ ├── flask
│ │ ├── static
│ │ │ └── flasky.css_t
│ │ └── theme.conf
│ ├── flask_small
│ │ ├── layout.html
│ │ ├── static
│ │ │ └── flasky.css_t
│ │ └── theme.conf
│ └── flask_theme_support.py
├── changelog.rst
├── conf.py
├── examples.rst
├── index.rst
└── make.bat
├── example
└── tumblelog
│ ├── Pipfile
│ ├── manage.py
│ ├── requirements.txt
│ └── tumblelog
│ ├── __init__.py
│ ├── admin.py
│ ├── forms.py
│ ├── models.py
│ ├── settings.py
│ ├── templates
│ ├── _delete.html
│ ├── _details.html
│ ├── _forms.html
│ ├── _messages.html
│ ├── base.html
│ └── tumblelog
│ │ └── post_list.html
│ ├── urls.py
│ ├── views.py
│ └── wsgi.py
├── pyproject.toml
├── pyrightconfig.json
├── pytest.ini
├── sg-rules
└── 0.6.0-rewrite-blank.yml
├── tests
├── __init__.py
├── forms
│ ├── __init__.py
│ ├── models.py
│ └── tests.py
├── settings.py
├── test_document_options.py
├── test_models.py
├── test_utils
│ ├── __init__.py
│ └── test_monkey.py
└── views
│ ├── __init__.py
│ ├── detail.py
│ ├── edit.py
│ ├── forms.py
│ ├── list.py
│ ├── models.py
│ ├── templates
│ ├── registration
│ │ └── login.html
│ └── views
│ │ ├── about.html
│ │ ├── apple_detail.html
│ │ ├── artist_detail.html
│ │ ├── artist_form.html
│ │ ├── author_confirm_delete.html
│ │ ├── author_detail.html
│ │ ├── author_form.html
│ │ ├── author_list.html
│ │ ├── author_objects.html
│ │ ├── author_view.html
│ │ ├── book_archive.html
│ │ ├── book_archive_day.html
│ │ ├── book_archive_month.html
│ │ ├── book_archive_week.html
│ │ ├── book_archive_year.html
│ │ ├── book_detail.html
│ │ ├── book_list.html
│ │ ├── confirm_delete.html
│ │ ├── detail.html
│ │ ├── form.html
│ │ ├── list.html
│ │ └── page_template.html
│ ├── tests.py
│ ├── urls.py
│ └── views.py
└── uv.lock
/.flake8:
--------------------------------------------------------------------------------
1 | # vim: set ft=dosini:
2 | [flake8]
3 | ignore =
4 | E501,
5 | E123,
6 | E221,
7 | E231,
8 | E241,
9 | E272,
10 | E731,
11 | N806,
12 | C901
13 | exclude =
14 | .git,
15 | __pycache__,
16 | dist,
17 | node_modules
18 | max-complexity = 10
19 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | push:
4 | tags:
5 | - v*
6 | env:
7 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
8 | jobs:
9 | Release:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v3
13 | - name: Set up Python
14 | uses: astral-sh/setup-uv@v5
15 | with:
16 | python-version: "3.10"
17 | - name: Build artifacts
18 | run: |
19 | uvx --from build python -m build --installer uv
20 | - name: Create release
21 | uses: softprops/action-gh-release@v2
22 | with:
23 | files: dist/*
24 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | # Docs:
2 | # - https://docs.github.com/en/actions/guides/about-service-containers
3 | # - https://github.com/actions/example-services/blob/main/.github/workflows/mongodb-service.yml
4 | name: CI
5 | on:
6 | push:
7 | branches:
8 | - master
9 | pull_request:
10 | jobs:
11 | Test:
12 | runs-on: ubuntu-latest
13 | strategy:
14 | matrix:
15 | python:
16 | - "3.9"
17 | - "3.10"
18 | - "3.11"
19 | - "3.12"
20 | - "3.13"
21 | - "pypy3.10"
22 | django:
23 | - "Django>=4.2,<4.3"
24 | - "Django>=5.0,<5.1"
25 | - "Django>=5.1,<5.2"
26 | exclude:
27 | - python: "3.9"
28 | django: "Django>=5.0,<5.1"
29 | - python: "3.9"
30 | django: "Django>=5.1,<5.2"
31 | env:
32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33 | DJANGO: ${{ matrix.django }}
34 | services:
35 | mongodb:
36 | image: mongo
37 | ports:
38 | - 27017:27017
39 | steps:
40 | - uses: actions/checkout@v3
41 | - name: Set up Python ${{ matrix.python-version }}
42 | uses: astral-sh/setup-uv@v5
43 | with:
44 | enable-cache: true
45 | python-version: ${{ matrix.python }}
46 | - name: Set up env
47 | run: |
48 | uv sync --group dev
49 | uv pip install -q "${{ matrix.django }}"
50 | - name: Run tests
51 | run: |
52 | uv run ruff check .
53 | uv run ruff format --check .
54 | uv run python -m pytest
55 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | !.gitignore
2 | *~
3 | *.py[co]
4 | .*.sw[po]
5 | *.egg
6 | docs/.build
7 | docs/_build
8 | build/
9 | dist/
10 | env/
11 | .venv
12 | Pipfile.lock
13 | .settings
14 | .project
15 | .coverage
16 | .pydevproject
17 | tests/bugfix.py
18 | *.egg-info
19 |
--------------------------------------------------------------------------------
/.versionrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "host": "github.com",
3 | "owner": "MongoEngine",
4 | "repository": "django-mongoengine",
5 | "bumpFiles": [
6 | {
7 | "filename": "VERSION",
8 | "type": "plain-text"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | Ross Lawley
2 | Jan Schrewe
3 | Wilson Júnior
4 | Maxime Verger-Del Bove
5 | Harry Marr
6 | Thiago Avelino
7 | Sergey Tereschenko
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ## [0.6.0-beta.5](https://github.com/MongoEngine/django-mongoengine/compare/v0.6.0-beta.4...v0.6.0-beta.5) (2025-04-20)
6 |
7 | ## [0.6.0-beta.4](https://github.com/MongoEngine/django-mongoengine/compare/v0.6.0-beta.3...v0.6.0-beta.4) (2025-04-20)
8 |
9 | ## [0.6.0-beta.3](https://github.com/MongoEngine/django-mongoengine/compare/v0.6.0-beta.2...v0.6.0-beta.3) (2024-12-28)
10 |
11 |
12 | ### Bug Fixes
13 |
14 | * Drop pypy 3.9 support ([cf5a89b](https://github.com/MongoEngine/django-mongoengine/commit/cf5a89bd0953091e9028cf18496c977ebe7e4145))
15 |
16 | ## [0.6.0-beta.2](https://github.com/MongoEngine/django-mongoengine/compare/v0.6.0-beta.1...v0.6.0-beta.2) (2023-11-16)
17 |
18 |
19 | ### Bug Fixes
20 |
21 | * Fix BooleanField ([e5a87c6](https://github.com/MongoEngine/django-mongoengine/commit/e5a87c6ed7b23018410903524915ad7a0e182ead))
22 |
23 | ## [0.6.0-beta.1](https://github.com/MongoEngine/django-mongoengine/compare/v0.6.0-beta.0...v0.6.0-beta.1) (2023-11-16)
24 |
25 |
26 | ### Features
27 |
28 | * Switch to ruff formatter ([3183d28](https://github.com/MongoEngine/django-mongoengine/commit/3183d286f152d418acf49ecee8b3c3dc49d6b53c))
29 |
30 | ## [0.6.0-beta.0](https://github.com/MongoEngine/django-mongoengine/compare/v0.5.7-beta.1...v0.6.0-beta.0) (2023-11-15)
31 |
32 |
33 | ### ⚠ BREAKING CHANGES
34 |
35 | * Removed `blank` argument support for fields, use mongoengine `required` instead.
36 | `ast-grep` can be used to automatically rewrite old code:
37 |
38 | ```
39 | ast-grep scan -r sg-rules/0.6.0-rewrite-blank.yml . -i
40 | ```
41 | Be careful to review, clean it up and test.
42 |
43 | * Switch to mongoengine style field arguments. Drop old django ([#200](https://github.com/MongoEngine/django-mongoengine/issues/200)) ([9d56f6a](https://github.com/MongoEngine/django-mongoengine/commit/9d56f6a58ccee93e2acae7af0a8d81d9aa43ae5b))
44 |
45 | ### [0.5.7-beta.1](https://github.com/MongoEngine/django-mongoengine/compare/v0.5.7-beta.0...v0.5.7-beta.1) (2023-11-04)
46 |
47 |
48 | ### Bug Fixes
49 |
50 | * Fix typing_extensions dependency ([6fa3394](https://github.com/MongoEngine/django-mongoengine/commit/6fa339407c32ae4b49589d9f5f1b1628c7458cf7))
51 |
52 | ### [0.5.7-beta.0](https://github.com/MongoEngine/django-mongoengine/compare/v0.5.6...v0.5.7-beta.0) (2023-11-04)
53 |
54 |
55 | ### Bug Fixes
56 |
57 | * More work on typing support; add black and ruff ([b9a0e0e](https://github.com/MongoEngine/django-mongoengine/commit/b9a0e0ec420faaec30a3d6a2f37d53ecf5461c6e))
58 |
59 | ### [0.5.6](https://github.com/MongoEngine/django-mongoengine/compare/v0.5.6-beta.0...v0.5.6) (2023-11-03)
60 |
61 |
62 | ### Bug Fixes
63 |
64 | * **typing:** Add static types for dynamic classes ([5c843e6](https://github.com/MongoEngine/django-mongoengine/commit/5c843e60f6cd26ebc232dac66b56c626a5808e73))
65 |
66 | ### [0.5.6-beta.0](https://github.com/MongoEngine/django-mongoengine/compare/v0.5.5...v0.5.6-beta.0) (2023-08-16)
67 |
68 |
69 | ### Features
70 |
71 | * Update test matrix; drop python 3.7 ([1bca66a](https://github.com/MongoEngine/django-mongoengine/commit/1bca66ad9238420790077c025424ff6c42cb61cb))
72 |
73 |
74 | ### Bug Fixes
75 |
76 | * Fix queryset comparison for django 4.2 ([da5f2b5](https://github.com/MongoEngine/django-mongoengine/commit/da5f2b5aab6a850a8217284bf6d24235db847edc))
77 |
78 | ### [0.5.5](https://github.com/MongoEngine/django-mongoengine/compare/v0.5.4...v0.5.5) (2023-08-10)
79 |
80 |
81 | ### Bug Fixes
82 |
83 | * **admin:** Use page_num without +1 (just like django does) ([#186](https://github.com/MongoEngine/django-mongoengine/issues/186)) ([8377bff](https://github.com/MongoEngine/django-mongoengine/commit/8377bff9126fb9a8409064e8ff0d87072ae6cb10))
84 |
85 | ### [0.5.4](https://github.com/MongoEngine/django-mongoengine/compare/v0.5.3...v0.5.4) (2022-05-27)
86 |
87 |
88 | ### Bug Fixes
89 |
90 | * Fix package metadata ([a2496b4](https://github.com/MongoEngine/django-mongoengine/commit/a2496b4854d67f843f1aae5e9ae0c36bf67a3c74))
91 |
92 | ### [0.5.3](https://github.com/MongoEngine/django-mongoengine/compare/v0.5.2...v0.5.3) (2022-05-27)
93 |
94 |
95 | ### Bug Fixes
96 |
97 | * Properly set attributes for auto created field ([3b9c039](https://github.com/MongoEngine/django-mongoengine/commit/3b9c039791991be2b01693c9c561fbcd82ad7564))
98 |
99 | ### [0.5.2](https://github.com/MongoEngine/django-mongoengine/compare/v0.5.1...v0.5.2) (2022-05-26)
100 |
101 |
102 | ### Bug Fixes
103 |
104 | * Fix create without id ([41bc837](https://github.com/MongoEngine/django-mongoengine/commit/41bc837916fca5f8226b4c1b0491db8766477f1f))
105 |
106 | ### [0.5.1](https://github.com/MongoEngine/django-mongoengine/compare/v0.5.0...v0.5.1) (2022-05-26)
107 |
108 |
109 | ### Bug Fixes
110 |
111 | * Add placeholders for get_path_to_parent/get_path_from_parent ([3653d21](https://github.com/MongoEngine/django-mongoengine/commit/3653d21b979d351b3dd411dbcd74d55e331fbddf))
112 | * Use django-patched ObjectIdField for auto created primary keys. ([5fde4a6](https://github.com/MongoEngine/django-mongoengine/commit/5fde4a6e17d11cab1e53d32a00b15b3ac55ef209))
113 |
114 | ## [0.5.0](https://github.com/MongoEngine/django-mongoengine/compare/v0.4.5...v0.5.0) (2022-05-14)
115 |
116 |
117 | ### Features
118 |
119 | * Clean up deprecated code for django-4 ([c82cbc6](https://github.com/MongoEngine/django-mongoengine/commit/c82cbc641a78c59e46b5b0e7be0e2867260e17c0)), closes [#156](https://github.com/MongoEngine/django-mongoengine/issues/156)
120 | * Drop django 2.2, add django 4.0 ([6c3d068](https://github.com/MongoEngine/django-mongoengine/commit/6c3d068b305a8f0ba88597ddeb97281a31f204fe))
121 |
122 |
123 | ### Bug Fixes
124 |
125 | * Fix 'StringField' object has no attribute 'flatchoices' ([#161](https://github.com/MongoEngine/django-mongoengine/issues/161)) ([db3860d](https://github.com/MongoEngine/django-mongoengine/commit/db3860d39fa29819fde71953e905f117f680a3be))
126 | * Fix invalid import EMPTY_CHANGELIST_VALUE in django >= 3 ([e8a75e8](https://github.com/MongoEngine/django-mongoengine/commit/e8a75e8e5860545ecfbadaf1b1285495022bd7cb))
127 | * **deps:** Move pytests dependency int dev-dependencies ([f7e37a7](https://github.com/MongoEngine/django-mongoengine/commit/f7e37a75e6612a4243ccc9abdb22f2dc72f53d9e))
128 |
129 | ### [0.4.6](https://github.com/MongoEngine/django-mongoengine/compare/v0.4.5...v0.4.6) (2021-04-11)
130 |
131 |
132 | ### Bug Fixes
133 |
134 | * **deps:** Move pytests dependency int dev-dependencies ([f7e37a7](https://github.com/MongoEngine/django-mongoengine/commit/f7e37a75e6612a4243ccc9abdb22f2dc72f53d9e))
135 |
136 | ### [0.4.5](https://github.com/MongoEngine/django-mongoengine/compare/v0.4.5-beta.4...v0.4.5) (2021-04-11)
137 |
138 |
139 | ### Features
140 |
141 | * Remove six ([773c791](https://github.com/MongoEngine/django-mongoengine/commit/773c79169b08ccd71f958e1855a2c47b1c7ebb7e))
142 | * **drf-spectacular:** AutoSchema generator for drf-spectacular support. ([2ef9529](https://github.com/MongoEngine/django-mongoengine/commit/2ef9529e330d482957acf582a56ac7aeabe853c6))
143 | * Support Django 3.2
144 |
145 | ### Bug Fixes
146 |
147 | * Add missing __init__.py ([bb11d36](https://github.com/MongoEngine/django-mongoengine/commit/bb11d36cf754bfd19bcff1e643fabd28c44f9e38))
148 |
149 |
150 | ### 2016-01-27
151 |
152 | * Support for django 1.9, minimum django version: 1.7
153 | * Moved `django_mongoengine.mongo_auth.MongoUser` to `django_mongoengine.mongo_auth.models.MongoUser`
154 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010-2012 See AUTHORS.
2 |
3 | Some rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are
7 | met:
8 |
9 | * Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above
13 | copyright notice, this list of conditions and the following
14 | disclaimer in the documentation and/or other materials provided
15 | with the distribution.
16 |
17 | * The names of the contributors may not be used to endorse or
18 | promote products derived from this software without specific
19 | prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include MANIFEST.in
2 | include README.rst
3 | include LICENSE
4 | include AUTHORS
5 | include VERSION
6 | recursive-include django_mongoengine *.html *.css *.js *.png *.gif *.woff *.ttf *.svg *.eot
7 | recursive-include docs *
8 | recursive-exclude docs *.pyc
9 | recursive-exclude docs *.pyo
10 | prune docs/_build
11 | prune docs/_themes/.git
12 | prune tests/
13 | prune example/
14 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | release:
2 | standard-version
3 |
4 | release-beta:
5 | standard-version -p beta
6 |
7 | publish:
8 | rm -rf dist
9 | python -m build --installer uv
10 | twine check dist/*
11 | twine upload dist/*
12 | git push --follow-tags
13 |
14 | test:
15 | pytest
16 |
17 | codegen:
18 | python codegen.py
19 | ruff format django_mongoengine/fields/__init__.py
20 | ruff django_mongoengine/ --fix # It doesn't work with filename.
21 |
--------------------------------------------------------------------------------
/OSSMETADATA:
--------------------------------------------------------------------------------
1 | osslifecycle=deprecated
2 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ==================
2 | Django-MongoEngine
3 | ==================
4 |
5 | |stand-with-ukraine|
6 |
7 | |lifecycle| |gitter|
8 |
9 | .. |lifecycle| image:: https://img.shields.io/osslifecycle/MongoEngine/django-mongoengine
10 | :alt: OSS Lifecycle
11 |
12 | .. |gitter| image:: https://badges.gitter.im/gitterHQ/gitter.png
13 | :target: https://gitter.im/MongoEngine/django-mongoengine
14 | :alt: Gitter chat
15 |
16 | .. |stand-with-ukraine| image:: https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg
17 | :target: https://stand-with-ukraine.pp.ua
18 | :alt: Stand With Ukraine
19 |
20 | THIS IS UNSTABLE PROJECT, IF YOU WANT TO USE IT - FIX WHAT YOU NEED
21 |
22 | Right now we're targeting to get things working on Django 4.2;
23 |
24 | WARNING: This project is not in good state, and is likely to break with django updates. Do not use it for any new projects.
25 |
26 | Recommended way for new projects is `django-mongodb-backend `_.
27 |
28 | Working / Django 4.2
29 | ------------------------
30 |
31 | * [ok] sessions
32 | * [ok] models/fields, fields needs testing
33 | * [ok] views
34 | * [ok] auth
35 | * [?] admin - partially working, some things broken
36 |
37 | Current status
38 | -------------------------------------------------------------------------------
39 |
40 | Many parts of projects rewritten/removed;
41 | Instead of copying django code i try to subclass/reuse/even monkey-patch;
42 | Everything listed above is working; admin - just base fuctions
43 | like changelist/edit, not tested with every form type; need's more work.
44 |
45 | Some code just plaholder to make things work;
46 | `django/forms/document_options.py` - dirty hack absolutely required to
47 | get thigs work with django. It replaces mongo _meta on model/class and
48 | provide django-like interface.
49 | It get's replaced after class creation via some metaclass magick.
50 |
51 | Fields notes
52 | ------------
53 |
54 | * Project uses mongoengine style argument `required=False`, not django style `blank=False`,
55 | to be compatible with mongo-types.
56 | **All your fields are optional by default.**
57 |
58 |
59 | TODO
60 | ----
61 |
62 | * Sync some files/docs that removed from mongoengine: https://github.com/seglberg/mongoengine/commit/a34f4c1beb93f430c37da20c8fd96ce02a0f20c1?diff=unified
63 | * Add docs for integrating: https://github.com/hmarr/django-debug-toolbar-mongo
64 | * Take a look at django-mongotools: https://github.com/wpjunior/django-mongotools
65 |
66 | Connecting
67 | ==========
68 |
69 | In your **settings.py** file, add following lines::
70 |
71 | MONGODB_DATABASES = {
72 | "default": {
73 | "name": database_name,
74 | "host": database_host,
75 | "password": database_password,
76 | "username": database_user,
77 | "tz_aware": True, # if you using timezones in django (USE_TZ = True)
78 | },
79 | }
80 |
81 | INSTALLED_APPS += ["django_mongoengine"]
82 |
83 | Documents
84 | =========
85 | Inhherit your documents from ``django_mongoengine.Document``,
86 | and define fields using ``django_mongoengine.fields``.::
87 |
88 | from django_mongoengine import Document, EmbeddedDocument, fields
89 |
90 | class Comment(EmbeddedDocument):
91 | created_at = fields.DateTimeField(
92 | default=datetime.datetime.now, editable=False,
93 | )
94 | author = fields.StringField(verbose_name="Name", max_length=255)
95 | email = fields.EmailField(verbose_name="Email")
96 | body = fields.StringField(verbose_name="Comment")
97 |
98 | class Post(Document):
99 | created_at = fields.DateTimeField(
100 | default=datetime.datetime.now, editable=False,
101 | )
102 | title = fields.StringField(max_length=255)
103 | slug = fields.StringField(max_length=255, primary_key=True)
104 | comments = fields.ListField(
105 | fields.EmbeddedDocumentField('Comment'), required=False,
106 | )
107 |
108 |
109 | Sessions
110 | ========
111 | Django allows the use of different backend stores for its sessions. MongoEngine
112 | provides a MongoDB-based session backend for Django, which allows you to use
113 | sessions in your Django application with just MongoDB. To enable the MongoEngine
114 | session backend, ensure that your settings module has
115 | ``'django.contrib.sessions.middleware.SessionMiddleware'`` in the
116 | ``MIDDLEWARE_CLASSES`` field and ``'django.contrib.sessions'`` in your
117 | ``INSTALLED_APPS``. From there, all you need to do is add the following line
118 | into your settings module::
119 |
120 | SESSION_ENGINE = 'django_mongoengine.sessions'
121 | SESSION_SERIALIZER = 'django_mongoengine.sessions.BSONSerializer'
122 |
123 | Django provides session cookie, which expires after
124 | ```SESSION_COOKIE_AGE``` seconds, but doesn't delete cookie at sessions
125 | backend, so ``'mongoengine.django.sessions'`` supports `mongodb TTL `_.
126 |
127 | .. note:: ``SESSION_SERIALIZER`` is only necessary in Django>1.6 as the default
128 | serializer is based around JSON and doesn't know how to convert
129 | ``bson.objectid.ObjectId`` instances to strings.
130 |
131 |
132 | How to run example app
133 | ----------------------
134 | .. code::
135 |
136 | uv sync --group dev
137 | uv pip install -r example/tumblelog/requirements.txt
138 | uv run python example/tumblelog/manage.py runserver
139 |
140 |
141 | How to run tests
142 | ----------------
143 | .. code::
144 |
145 | uv run python -m pytest
146 |
--------------------------------------------------------------------------------
/codegen.py:
--------------------------------------------------------------------------------
1 | def generate_fields():
2 | """
3 | Typing support cannot handle monkey-patching at runtime, so we need to generate fields explicitly.
4 | """
5 | from mongoengine import fields
6 | from django_mongoengine.fields import djangoflavor as mixins
7 |
8 | fields_code = str(_fields)
9 | for fname in fields.__all__:
10 | mixin_name = fname if hasattr(mixins, fname) else "DjangoField"
11 | fields_code += f"class {fname}(_mixins.{mixin_name}, _fields.{fname}):\n pass\n"
12 |
13 | return fields_code
14 |
15 |
16 | _fields = """
17 | from mongoengine import fields as _fields
18 | from . import djangoflavor as _mixins
19 | from django_mongoengine.utils.monkey import patch_mongoengine_field
20 |
21 | for f in ["StringField", "ObjectIdField"]:
22 | patch_mongoengine_field(f)
23 |
24 | """
25 |
26 | if __name__ == "__main__":
27 | fname = "django_mongoengine/fields/__init__.py"
28 | # This content required, because otherwise mixins import does not work.
29 | open(fname, "w").write("from mongoengine.fields import *")
30 | content = generate_fields()
31 | open(fname, "w").write(content)
32 |
--------------------------------------------------------------------------------
/django_mongoengine/__init__.py:
--------------------------------------------------------------------------------
1 | from .document import Document, DynamicDocument, DynamicEmbeddedDocument, EmbeddedDocument
2 | from .queryset import QuerySet, QuerySetNoCache
3 |
4 | __all__ = [
5 | "QuerySet",
6 | "QuerySetNoCache",
7 | "Document",
8 | "DynamicDocument",
9 | "EmbeddedDocument",
10 | "DynamicEmbeddedDocument",
11 | ]
12 |
13 | try:
14 | from django import VERSION as _django_version
15 |
16 | if _django_version < (3, 2):
17 | default_app_config = "django_mongoengine.apps.DjangoMongoEngineConfig"
18 | except ImportError:
19 | pass
20 |
--------------------------------------------------------------------------------
/django_mongoengine/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 | from django.conf import settings
3 | from django.core.exceptions import ImproperlyConfigured
4 | from mongoengine import connection
5 |
6 |
7 | class DjangoMongoEngineConfig(AppConfig):
8 | """Simple AppConfig which does not do automatic discovery."""
9 |
10 | name = "django_mongoengine"
11 | verbose_name = "Django-MongoEngine"
12 |
13 | def ready(self):
14 | if not hasattr(settings, "MONGODB_DATABASES"):
15 | raise ImproperlyConfigured("Missing `MONGODB_DATABASES` in settings.py")
16 |
17 | for alias, conn_settings in settings.MONGODB_DATABASES.items():
18 | connection.register_connection(alias, **conn_settings)
19 |
--------------------------------------------------------------------------------
/django_mongoengine/contrib/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MongoEngine/django-mongoengine/298cdcf59b21fe590958f57f27bf28e7192fcc4e/django_mongoengine/contrib/__init__.py
--------------------------------------------------------------------------------
/django_mongoengine/contrib/drf_spectacular.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from drf_spectacular import openapi
3 | from drf_spectacular.openapi import OpenApiTypes, build_basic_type
4 | from mongoengine.fields import BaseField
5 | from rest_framework import serializers
6 |
7 | from django_mongoengine.forms.document_options import PkWrapper
8 |
9 | SUPPORTED_FIELDS = (PkWrapper, BaseField)
10 |
11 |
12 | class AutoSchema(openapi.AutoSchema):
13 | """
14 | Schema supporting basic MongoEngine integration,
15 | while falling back to default for not django-mongoengine fields.
16 | """
17 |
18 | def _map_model_field(self, model_field, direction):
19 | if isinstance(model_field, SUPPORTED_FIELDS):
20 | if hasattr(model_field, "get_internal_type"):
21 | internal_type = getattr(models, model_field.get_internal_type())
22 | field_cls = serializers.ModelSerializer.serializer_field_mapping.get(internal_type)
23 | if field_cls:
24 | return self._map_serializer_field(field_cls(), direction)
25 | return build_basic_type(OpenApiTypes.STR)
26 | return super()._map_model_field(model_field, direction)
27 |
--------------------------------------------------------------------------------
/django_mongoengine/document.py:
--------------------------------------------------------------------------------
1 | # ruff: noqa: F811
2 | from __future__ import annotations
3 |
4 | from functools import partial
5 | from typing import TYPE_CHECKING, Any
6 |
7 | from bson.objectid import ObjectId
8 | from django.db.models import Model
9 | from django.db.models.base import ModelState
10 | from mongoengine import DoesNotExist
11 | from mongoengine import document as me
12 | from mongoengine.base import metaclasses as mtc
13 | from mongoengine.errors import FieldDoesNotExist
14 |
15 | from .fields import ObjectIdField
16 | from .forms.document_options import DocumentMetaWrapper
17 | from .queryset import QuerySetManager
18 |
19 | if TYPE_CHECKING:
20 | from mongoengine.fields import StringField
21 | from typing_extensions import Self
22 |
23 | # TopLevelDocumentMetaclass is using ObjectIdField to create default pk field,
24 | # if one's not set explicitly.
25 | # We need to know it's not editable and auto_created.
26 | mtc.ObjectIdField = partial(ObjectIdField, editable=False, auto_created=True)
27 |
28 |
29 | def django_meta(meta, *top_bases):
30 | class metaclass(meta):
31 | def __new__(cls, name, bases, attrs):
32 | change_bases = len(bases) == 1 and (bases[0].__name__ == "temporary_meta")
33 | if change_bases:
34 | new_bases = top_bases
35 | else:
36 | new_bases = ()
37 | for b in bases:
38 | if getattr(b, "swap_base", False):
39 | new_bases += top_bases
40 | else:
41 | new_bases += (b,)
42 | new_cls = meta.__new__(cls, name, new_bases, attrs)
43 | new_cls._meta = DocumentMetaWrapper(new_cls)
44 | return new_cls
45 |
46 | return type.__new__(metaclass, "temporary_meta", (), {})
47 |
48 |
49 | class DjangoFlavor:
50 | id: StringField
51 | objects = QuerySetManager["Self"]()
52 | _default_manager = QuerySetManager["Self"]()
53 | _get_pk_val = Model.__dict__["_get_pk_val"]
54 | _meta: DocumentMetaWrapper | dict[str, Any]
55 | DoesNotExist: type[DoesNotExist]
56 |
57 | def __init__(self, *args, **kwargs):
58 | self._state = ModelState()
59 | self._state.db = self._meta.get("db_alias", me.DEFAULT_CONNECTION_NAME)
60 | super().__init__(*args, **kwargs)
61 |
62 | def _get_unique_checks(self, exclude=None):
63 | # XXX: source: django/db/models/base.py
64 | # used in modelform validation
65 | unique_checks, date_checks = [], []
66 | return unique_checks, date_checks
67 |
68 | def serializable_value(self, field_name):
69 | """
70 | Returns the value of the field name for this instance. If the field is
71 | a foreign key, returns the id value, instead of the object. If there's
72 | no Field object with this name on the model, the model attribute's
73 | value is returned directly.
74 |
75 | Used to serialize a field's value (in the serializer, or form output,
76 | for example). Normally, you would just access the attribute directly
77 | and not use this method.
78 | """
79 | try:
80 | field = self._meta.get_field(field_name)
81 | except FieldDoesNotExist:
82 | return getattr(self, field_name)
83 | value = field.to_mongo(getattr(self, field.name))
84 | if isinstance(value, ObjectId):
85 | return str(value)
86 | return value
87 |
88 |
89 | class Document(
90 | django_meta(
91 | mtc.TopLevelDocumentMetaclass,
92 | DjangoFlavor,
93 | me.Document,
94 | )
95 | ):
96 | swap_base = True
97 |
98 |
99 | class DynamicDocument(django_meta(mtc.TopLevelDocumentMetaclass, DjangoFlavor, me.DynamicDocument)):
100 | swap_base = True
101 |
102 |
103 | class EmbeddedDocument(django_meta(mtc.DocumentMetaclass, DjangoFlavor, me.EmbeddedDocument)):
104 | swap_base = True
105 |
106 |
107 | class DynamicEmbeddedDocument(
108 | django_meta(mtc.DocumentMetaclass, DjangoFlavor, me.DynamicEmbeddedDocument)
109 | ):
110 | swap_base = True
111 |
112 |
113 | if TYPE_CHECKING:
114 |
115 | class Document(DjangoFlavor, me.Document): ...
116 |
117 | class DynamicDocument(DjangoFlavor, me.DynamicDocument): ...
118 |
119 | class EmbeddedDocument(DjangoFlavor, me.EmbeddedDocument):
120 | _instance: Document
121 |
122 | class DynamicEmbeddedDocument(DjangoFlavor, me.DynamicEmbeddedDocument): ...
123 |
--------------------------------------------------------------------------------
/django_mongoengine/fields/__init__.py:
--------------------------------------------------------------------------------
1 | from mongoengine import fields as _fields
2 | from . import djangoflavor as _mixins
3 | from django_mongoengine.utils.monkey import patch_mongoengine_field
4 |
5 | for f in ["StringField", "ObjectIdField"]:
6 | patch_mongoengine_field(f)
7 |
8 |
9 | class StringField(_mixins.StringField, _fields.StringField):
10 | pass
11 |
12 |
13 | class URLField(_mixins.URLField, _fields.URLField):
14 | pass
15 |
16 |
17 | class EmailField(_mixins.EmailField, _fields.EmailField):
18 | pass
19 |
20 |
21 | class IntField(_mixins.IntField, _fields.IntField):
22 | pass
23 |
24 |
25 | class LongField(_mixins.DjangoField, _fields.LongField):
26 | pass
27 |
28 |
29 | class FloatField(_mixins.FloatField, _fields.FloatField):
30 | pass
31 |
32 |
33 | class DecimalField(_mixins.DecimalField, _fields.DecimalField):
34 | pass
35 |
36 |
37 | class BooleanField(_mixins.BooleanField, _fields.BooleanField):
38 | pass
39 |
40 |
41 | class DateTimeField(_mixins.DateTimeField, _fields.DateTimeField):
42 | pass
43 |
44 |
45 | class DateField(_mixins.DjangoField, _fields.DateField):
46 | pass
47 |
48 |
49 | class ComplexDateTimeField(_mixins.DjangoField, _fields.ComplexDateTimeField):
50 | pass
51 |
52 |
53 | class EmbeddedDocumentField(_mixins.EmbeddedDocumentField, _fields.EmbeddedDocumentField):
54 | pass
55 |
56 |
57 | class ObjectIdField(_mixins.DjangoField, _fields.ObjectIdField):
58 | pass
59 |
60 |
61 | class GenericEmbeddedDocumentField(_mixins.DjangoField, _fields.GenericEmbeddedDocumentField):
62 | pass
63 |
64 |
65 | class DynamicField(_mixins.DjangoField, _fields.DynamicField):
66 | pass
67 |
68 |
69 | class ListField(_mixins.ListField, _fields.ListField):
70 | pass
71 |
72 |
73 | class SortedListField(_mixins.DjangoField, _fields.SortedListField):
74 | pass
75 |
76 |
77 | class EmbeddedDocumentListField(_mixins.DjangoField, _fields.EmbeddedDocumentListField):
78 | pass
79 |
80 |
81 | class DictField(_mixins.DictField, _fields.DictField):
82 | pass
83 |
84 |
85 | class MapField(_mixins.DjangoField, _fields.MapField):
86 | pass
87 |
88 |
89 | class ReferenceField(_mixins.ReferenceField, _fields.ReferenceField):
90 | pass
91 |
92 |
93 | class CachedReferenceField(_mixins.DjangoField, _fields.CachedReferenceField):
94 | pass
95 |
96 |
97 | class LazyReferenceField(_mixins.DjangoField, _fields.LazyReferenceField):
98 | pass
99 |
100 |
101 | class GenericLazyReferenceField(_mixins.DjangoField, _fields.GenericLazyReferenceField):
102 | pass
103 |
104 |
105 | class GenericReferenceField(_mixins.DjangoField, _fields.GenericReferenceField):
106 | pass
107 |
108 |
109 | class BinaryField(_mixins.DjangoField, _fields.BinaryField):
110 | pass
111 |
112 |
113 | class GridFSError(_mixins.DjangoField, _fields.GridFSError):
114 | pass
115 |
116 |
117 | class GridFSProxy(_mixins.DjangoField, _fields.GridFSProxy):
118 | pass
119 |
120 |
121 | class FileField(_mixins.FileField, _fields.FileField):
122 | pass
123 |
124 |
125 | class ImageGridFsProxy(_mixins.DjangoField, _fields.ImageGridFsProxy):
126 | pass
127 |
128 |
129 | class ImproperlyConfigured(_mixins.ImproperlyConfigured, _fields.ImproperlyConfigured):
130 | pass
131 |
132 |
133 | class ImageField(_mixins.ImageField, _fields.ImageField):
134 | pass
135 |
136 |
137 | class GeoPointField(_mixins.DjangoField, _fields.GeoPointField):
138 | pass
139 |
140 |
141 | class PointField(_mixins.DjangoField, _fields.PointField):
142 | pass
143 |
144 |
145 | class LineStringField(_mixins.DjangoField, _fields.LineStringField):
146 | pass
147 |
148 |
149 | class PolygonField(_mixins.DjangoField, _fields.PolygonField):
150 | pass
151 |
152 |
153 | class SequenceField(_mixins.DjangoField, _fields.SequenceField):
154 | pass
155 |
156 |
157 | class UUIDField(_mixins.DjangoField, _fields.UUIDField):
158 | pass
159 |
160 |
161 | class EnumField(_mixins.DjangoField, _fields.EnumField):
162 | pass
163 |
164 |
165 | class MultiPointField(_mixins.DjangoField, _fields.MultiPointField):
166 | pass
167 |
168 |
169 | class MultiLineStringField(_mixins.DjangoField, _fields.MultiLineStringField):
170 | pass
171 |
172 |
173 | class MultiPolygonField(_mixins.DjangoField, _fields.MultiPolygonField):
174 | pass
175 |
176 |
177 | class GeoJsonBaseField(_mixins.DjangoField, _fields.GeoJsonBaseField):
178 | pass
179 |
180 |
181 | class Decimal128Field(_mixins.DjangoField, _fields.Decimal128Field):
182 | pass
183 |
--------------------------------------------------------------------------------
/django_mongoengine/fields/internal.py:
--------------------------------------------------------------------------------
1 | INTERNAL_DJANGO_FIELDS_MAP = {
2 | "StringField": "CharField",
3 | }
4 |
--------------------------------------------------------------------------------
/django_mongoengine/forms/__init__.py:
--------------------------------------------------------------------------------
1 | from .documents import * # noqa: F403
2 | from .utils import * # noqa: F403
3 |
--------------------------------------------------------------------------------
/django_mongoengine/forms/document_options.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | from django.core.exceptions import FieldDoesNotExist
4 | from django.db.models.options import Options
5 | from django.utils.encoding import smart_str
6 | from django.utils.text import capfirst
7 |
8 | try:
9 | from django.db.models.options import get_verbose_name as camel_case_to_spaces
10 | except ImportError:
11 | from django.utils.text import camel_case_to_spaces, format_lazy
12 |
13 | from mongoengine.fields import ReferenceField
14 |
15 |
16 | class PkWrapper:
17 | """Used to wrap the Primary Key so it can mimic Django's expectations"""
18 |
19 | editable = False
20 | remote_field = None
21 |
22 | def __init__(self, wrapped):
23 | self.obj = wrapped
24 |
25 | def __getattr__(self, attr):
26 | if attr in dir(self.obj):
27 | return getattr(self.obj, attr)
28 | raise AttributeError(f"{self} has no {attr}")
29 |
30 | def __setattr__(self, attr, value):
31 | if attr != "obj" and hasattr(self.obj, attr):
32 | setattr(self.obj, attr, value)
33 | super().__setattr__(attr, value)
34 |
35 | def value_to_string(self, obj):
36 | """
37 | Returns a string value of this field from the passed obj.
38 | This is used by the serialization framework.
39 | """
40 | return smart_str(obj.pk)
41 |
42 | def get_internal_type(self):
43 | return "CharField"
44 |
45 |
46 | class DocumentMetaWrapper:
47 | """
48 | Used to store mongoengine's _meta dict to make the document admin
49 | as compatible as possible to django's meta class on models.
50 | """
51 |
52 | _pk = None
53 | pk_name = None
54 | object_name = None
55 | model_name = None
56 | verbose_name = None
57 | has_auto_field = False
58 | abstract = False
59 | object_name = None
60 | proxy = []
61 | virtual_fields = []
62 | concrete_fields = []
63 | proxied_children = []
64 | parents = {}
65 | private_fields = []
66 | many_to_many = []
67 | related_fkey_lookups = []
68 | swapped = False
69 | _field_cache = None
70 | document = None
71 | _meta = None
72 | try:
73 | default_apps = Options.default_apps
74 | except AttributeError:
75 | pass
76 | app_config = Options.__dict__["app_config"]
77 | try:
78 | _property_names = Options.__dict__["_property_names"]
79 | except KeyError:
80 | pass
81 |
82 | def __init__(self, document):
83 | if isinstance(document._meta, DocumentMetaWrapper):
84 | meta = document._meta._meta
85 | else:
86 | meta = document._meta
87 | self.document = document
88 | self._meta = meta or {}
89 | self.model = document
90 | self.concrete_model = document
91 | self.concrete_fields = document._fields.values()
92 | self.object_name = document.__name__
93 | self.model_name = self.object_name.lower()
94 | self.fields = self.concrete_fields
95 | try:
96 | self.apps = self.default_apps
97 | except AttributeError:
98 | pass
99 | try:
100 | self.object_name = self.document.__name__
101 | except AttributeError:
102 | self.object_name = self.document.__class__.__name__
103 |
104 | self.verbose_name = self.get_verbose_name()
105 |
106 | # EmbeddedDocuments don't have an id field.
107 | try:
108 | self.pk_name = self._meta["id_field"]
109 | self._init_pk()
110 | except KeyError:
111 | pass
112 |
113 | @property
114 | def label(self):
115 | return "%s.%s" % (self.app_label, self.model_name)
116 |
117 | @property
118 | def label_lower(self):
119 | return "%s.%s" % (self.app_label, self.model_name)
120 |
121 | @property
122 | def app_label(self) -> str:
123 | if "app_label" in self._meta:
124 | return self._meta["app_label"]
125 | else:
126 | model_module = sys.modules[self.document.__module__]
127 | return model_module.__name__.split(".")[-2]
128 |
129 | def get_path_to_parent(self, parent):
130 | # This is just placeholders.
131 | # If you depend on this, port it from django.
132 | return []
133 |
134 | def get_path_from_parent(self, parent):
135 | return []
136 |
137 | def get_verbose_name(self):
138 | """
139 | Returns the verbose name of the document.
140 |
141 | Checks the original meta dict first. If it is not found
142 | then generates a verbose name from from the object name.
143 | """
144 | if "verbose_name" in self._meta:
145 | return self._meta["verbose_name"]
146 | else:
147 | return capfirst(camel_case_to_spaces(self.object_name))
148 |
149 | @property
150 | def verbose_name_raw(self):
151 | return str(self.verbose_name)
152 |
153 | @property
154 | def verbose_name_plural(self):
155 | if "verbose_name_plural" in self._meta:
156 | return self.meta["verbose_name_plural"]
157 | else:
158 | return format_lazy("{}s", self.verbose_name)
159 |
160 | @property
161 | def pk(self):
162 | if not hasattr(self._pk, "attname"):
163 | self._init_pk()
164 | return self._pk
165 |
166 | def get_fields(self, include_parents=True, include_hidden=False):
167 | # XXX: simple placeholder; TODO: handle options;
168 | return self.concrete_fields
169 |
170 | def _init_pk(self):
171 | """
172 | Adds a wrapper around the documents pk field. The wrapper object gets the attributes
173 | django expects on the pk field, like name and attname.
174 |
175 | The function also adds a _get_pk_val method to the document.
176 | """
177 | if self.id_field is None:
178 | return
179 |
180 | try:
181 | pk_field = getattr(self.document, self.id_field)
182 | self._pk = PkWrapper(pk_field)
183 | self._pk.name = self.id_field
184 | self._pk.attname = self.id_field
185 | except AttributeError:
186 | return
187 |
188 | def get_add_permission(self):
189 | return "add_%s" % self.object_name.lower()
190 |
191 | def get_change_permission(self):
192 | return "change_%s" % self.object_name.lower()
193 |
194 | def get_delete_permission(self):
195 | return "delete_%s" % self.object_name.lower()
196 |
197 | def get_ordered_objects(self):
198 | return []
199 |
200 | def get_field_by_name(self, name):
201 | """
202 | Returns the (field_object, model, direct, m2m), where field_object is
203 | the Field instance for the given name, model is the model containing
204 | this field (None for local fields), direct is True if the field exists
205 | on this model, and m2m is True for many-to-many relations. When
206 | 'direct' is False, 'field_object' is the corresponding RelatedObject
207 | for this field (since the field doesn't have an instance associated
208 | with it).
209 |
210 | Uses a cache internally, so after the first access, this is very fast.
211 | """
212 | try:
213 | try:
214 | return self._field_cache[name]
215 | except TypeError:
216 | self._init_field_cache()
217 | return self._field_cache[name]
218 | except KeyError:
219 | raise FieldDoesNotExist("%s has no field named %r" % (self.object_name, name))
220 |
221 | def _init_field_cache(self):
222 | if self._field_cache is None:
223 | self._field_cache = {}
224 |
225 | for f in self.document._fields.values():
226 | if isinstance(f, ReferenceField):
227 | document = f.document_type
228 | self._field_cache[document._meta.model_name] = (f, document, False, False)
229 | else:
230 | self._field_cache[f.name] = (f, None, True, False)
231 |
232 | return self._field_cache
233 |
234 | def get_field(self, name, many_to_many=True):
235 | """
236 | Returns the requested field by name. Raises FieldDoesNotExist on error.
237 | """
238 | return self.get_field_by_name(name)[0]
239 |
240 | def __getattr__(self, name):
241 | try:
242 | return self._meta[name]
243 | except KeyError as e:
244 | raise AttributeError(*e.args)
245 |
246 | def __setattr__(self, name, value):
247 | if not hasattr(self, name):
248 | self._meta[name] = value
249 | else:
250 | super().__setattr__(name, value)
251 |
252 | def __getitem__(self, key):
253 | return self._meta[key]
254 |
255 | def __setitem__(self, key, value):
256 | self._meta[key] = value
257 |
258 | def __contains__(self, key):
259 | return key in self._meta
260 |
261 | def get(self, key, default=None):
262 | try:
263 | return self.__getitem__(key)
264 | except KeyError:
265 | return default
266 |
267 | def get_parent_list(self):
268 | return []
269 |
270 | def get_all_related_objects(self, *args, **kwargs):
271 | return []
272 |
273 | def iteritems(self):
274 | return self._meta.iteritems()
275 |
276 | def items(self):
277 | return self._meta.items()
278 |
--------------------------------------------------------------------------------
/django_mongoengine/forms/fields.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from django.core.exceptions import ValidationError
3 | from django.utils.translation import gettext_lazy as _
4 |
5 | from django_mongoengine.forms.widgets import Dictionary, EmbeddedFieldWidget
6 |
7 |
8 | class ReferenceField(forms.ModelChoiceField):
9 | pass
10 |
11 |
12 | class DocumentMultipleChoiceField(forms.ModelMultipleChoiceField):
13 | pass
14 |
15 |
16 | class DictField(forms.Field):
17 | """
18 | DictField for mongo forms
19 | """
20 |
21 | error_messages = {
22 | "length": _("Ensure the keys length is less than or equal to %s."),
23 | "invalid_key": _("Ensure the keys are not : %s."),
24 | "illegal": _("Ensure the keys does not contain any illegal character : %s."),
25 | "depth": _("Ensure the dictionary depth is less than or equal to %s."),
26 | }
27 |
28 | # Mongo reserved keywords
29 | invalid_keys = ["err", "errmsg"]
30 | # Mongo prohibit . in keys
31 | illegal_characters = ["."]
32 | # limit key length for efficiency
33 | key_limit = 200
34 | # limit depth for dictionaries
35 | max_depth = None
36 |
37 | def __init__(self, max_depth=None, flags=None, sub_attrs=None, attrs=None, *args, **kwargs):
38 | if "error_messages" in kwargs.keys():
39 | kwargs["error_messages"].update(self.error_messages)
40 | else:
41 | kwargs["error_messages"] = self.error_messages
42 |
43 | self.max_depth = max_depth if max_depth and max_depth >= 0 else None
44 |
45 | if "widget" not in kwargs.keys():
46 | schema = None
47 | # Here it needs to be clearer, because this is only useful when creating an object,
48 | # if no default value is provided, default is callable
49 | if "initial" in kwargs and not callable(kwargs["initial"]):
50 | if isinstance(kwargs["initial"], dict):
51 | schema = kwargs["initial"]
52 |
53 | # here if other parameters are passed, like max_depth and flags, then we hand them to the dict
54 | kwargs["widget"] = Dictionary(
55 | max_depth=max_depth, flags=flags, schema=schema, sub_attrs=sub_attrs
56 | )
57 |
58 | super().__init__(*args, **kwargs)
59 |
60 | def prepare_value(self, value):
61 | return value
62 |
63 | def to_python(self, value):
64 | value = self.get_dict(value)
65 | return value
66 |
67 | def clean(self, value):
68 | self.max_depth = self.widget.max_depth
69 | value = self.to_python(value)
70 | self.validate(value)
71 | return value
72 |
73 | def get_dict(self, a_list):
74 | """
75 | A function that return a dictionary from a list of lists, with any depth
76 | """
77 | d = {}
78 | for k in a_list:
79 | if isinstance(k, list):
80 | if isinstance(k[1], list) and k[0]:
81 | d.update({k[0]: self.get_dict(k[1])})
82 | elif k[0]:
83 | d.update({k[0]: k[1]})
84 | return d
85 |
86 | def validate(self, value, depth=0):
87 | # we should not use the super.validate method
88 | if self.max_depth is not None and depth > self.max_depth:
89 | raise ValidationError(self.error_messages["depth"] % self.max_depth)
90 | for k, v in value.items():
91 | self.run_validators(k)
92 | if k in self.invalid_keys:
93 | raise ValidationError(self.error_messages["invalid_key"] % self.invalid_keys)
94 | if len(k) > self.key_limit:
95 | raise ValidationError(self.error_messages["length"] % self.key_limit)
96 | for u in self.illegal_characters:
97 | if u in k:
98 | raise ValidationError(self.error_messages["illegal"] % self.illegal_characters)
99 | if isinstance(v, dict):
100 | self.validate(v, depth + 1)
101 |
102 |
103 | class EmbeddedDocumentField(forms.MultiValueField):
104 | def __init__(self, form, *args, **kwargs):
105 | self.form = form()
106 | # Set the widget and initial data
107 | kwargs["widget"] = EmbeddedFieldWidget(self.form.fields)
108 | kwargs["initial"] = [f.initial for f in self.form.fields.values()]
109 | kwargs["require_all_fields"] = False
110 | super().__init__(fields=tuple(self.form.fields.values()), *args, **kwargs)
111 |
112 | def bound_data(self, data, initial):
113 | return data
114 |
115 | def prepare_value(self, value):
116 | return value
117 |
118 | def compress(self, data_list):
119 | data = {}
120 | if data_list:
121 | data = {f.name: data_list[i] for i, f in enumerate(self.form)}
122 | f = self.form.__class__(data)
123 | f.is_valid()
124 | return f.cleaned_data
125 | return data
126 |
127 | def clean(self, value):
128 | return self.to_python(super().clean(value))
129 |
130 | def to_python(self, value):
131 | obj = self.form._meta.model()
132 | [obj.__setattr__(k, value[k]) for k in value.keys()]
133 | return obj
134 |
--------------------------------------------------------------------------------
/django_mongoengine/forms/utils.py:
--------------------------------------------------------------------------------
1 | from collections import OrderedDict
2 | from functools import partial
3 |
4 | from django.forms.fields import Field
5 |
6 |
7 | def patch_document(function, instance):
8 | setattr(instance, function.__name__, partial(function, instance))
9 |
10 |
11 | def get_declared_fields(bases, attrs, with_base_fields=True):
12 | """
13 | Create a list of form field instances from the passed in 'attrs', plus any
14 | similar fields on the base classes (in 'bases'). This is used by both the
15 | Form and ModelForm metaclasses.
16 |
17 | If 'with_base_fields' is True, all fields from the bases are used.
18 | Otherwise, only fields in the 'declared_fields' attribute on the bases are
19 | used. The distinction is useful in ModelForm subclassing.
20 | Also integrates any additional media definitions.
21 | """
22 |
23 | fields = [
24 | (field_name, attrs.pop(field_name))
25 | for field_name, obj in attrs.items()
26 | if isinstance(obj, Field)
27 | ]
28 | fields.sort(key=lambda x: x[1].creation_counter)
29 |
30 | # If this class is subclassing another Form, add that Form's fields.
31 | # Note that we loop over the bases in *reverse*. This is necessary in
32 | # order to preserve the correct order of fields.
33 | if with_base_fields:
34 | for base in bases[::-1]:
35 | if hasattr(base, "base_fields"):
36 | fields = list(base.base_fields.items()) + fields
37 | else:
38 | for base in bases[::-1]:
39 | if hasattr(base, "declared_fields"):
40 | fields = list(base.declared_fields.items()) + fields
41 |
42 | return OrderedDict(fields)
43 |
--------------------------------------------------------------------------------
/django_mongoengine/mongo_admin/__init__.py:
--------------------------------------------------------------------------------
1 | from django.utils.module_loading import autodiscover_modules
2 |
3 | from .decorators import register
4 | from .options import DocumentAdmin
5 | from .sites import site
6 |
7 | __all__ = ["DocumentAdmin", "site", "register"]
8 |
9 |
10 | def autodiscover():
11 | autodiscover_modules("admin", register_to=site)
12 |
13 |
14 | default_app_config = "django_mongoengine.mongo_admin.apps.MongoAdminConfig"
15 |
--------------------------------------------------------------------------------
/django_mongoengine/mongo_admin/actions.py:
--------------------------------------------------------------------------------
1 | """
2 | Built-in, globally-available admin actions.
3 | """
4 |
5 | from django import template
6 | from django.contrib.admin import helpers
7 | from django.contrib.admin.actions import delete_selected as django_delete_selected
8 | from django.contrib.admin.utils import get_deleted_objects, model_ngettext
9 | from django.core.exceptions import PermissionDenied
10 | from django.db import models
11 | from django.shortcuts import render_to_response
12 | from django.utils.translation import gettext_lazy as _
13 |
14 | from django_mongoengine.utils import force_str
15 |
16 |
17 | def delete_selected(modeladmin, request, queryset):
18 | if issubclass(modeladmin.model, models.Model):
19 | return django_delete_selected(modeladmin, request, queryset)
20 | else:
21 | return _delete_selected(modeladmin, request, queryset)
22 |
23 |
24 | def _delete_selected(modeladmin, request, queryset):
25 | """
26 | Default action which deletes the selected objects.
27 |
28 | This action first displays a confirmation page whichs shows all the
29 | deleteable objects, or, if the user has no permission one of the related
30 | childs (foreignkeys), a "permission denied" message.
31 |
32 | Next, it delets all selected objects and redirects back to the change list.
33 | """
34 | opts = modeladmin.model._meta
35 | app_label = opts.app_label
36 |
37 | # Check that the user has delete permission for the actual model
38 | if not modeladmin.has_delete_permission(request):
39 | raise PermissionDenied
40 |
41 | # Populate deletable_objects, a data structure of all related objects that
42 | # will also be deleted.
43 | # TODO: Permissions would be so cool...
44 | deletable_objects, perms_needed, protected = get_deleted_objects(
45 | queryset, request, modeladmin.admin_site
46 | )
47 |
48 | # The user has already confirmed the deletion.
49 | # Do the deletion and return a None to display the change list view again.
50 | if request.POST.get("post"):
51 | if perms_needed:
52 | raise PermissionDenied
53 | n = len(queryset)
54 | if n:
55 | for obj in queryset:
56 | obj_display = force_str(obj)
57 | modeladmin.log_deletion(request, obj, obj_display)
58 | # call the objects delete method to ensure signals are
59 | # processed.
60 | obj.delete()
61 | # This is what you get if you have to monkey patch every object in a changelist
62 | # No queryset object, I can tell ya. So we get a new one and delete that.
63 | # pk_list = [o.pk for o in queryset]
64 | # klass = queryset[0].__class__
65 | # qs = klass.objects.filter(pk__in=pk_list)
66 | # qs.delete()
67 | modeladmin.message_user(
68 | request,
69 | _("Successfully deleted %(count)d %(items)s.")
70 | % {"count": n, "items": model_ngettext(modeladmin.opts, n)},
71 | )
72 | # Return None to display the change list page again.
73 | return None
74 |
75 | if len(queryset) == 1:
76 | objects_name = force_str(opts.verbose_name)
77 | else:
78 | objects_name = force_str(opts.verbose_name_plural)
79 |
80 | if perms_needed or protected:
81 | title = _("Cannot delete %(name)s") % {"name": objects_name}
82 | else:
83 | title = _("Are you sure?")
84 |
85 | context = {
86 | "title": title,
87 | "objects_name": objects_name,
88 | "deletable_objects": [deletable_objects],
89 | "queryset": queryset,
90 | "perms_lacking": perms_needed,
91 | "protected": protected,
92 | "opts": opts,
93 | "root_path": modeladmin.admin_site.root_path,
94 | "app_label": app_label,
95 | "action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
96 | }
97 |
98 | # Display the confirmation page
99 | return render_to_response(
100 | modeladmin.delete_selected_confirmation_template
101 | or [
102 | "admin/%s/%s/delete_selected_confirmation.html" % (app_label, opts.object_name.lower()),
103 | "admin/%s/delete_selected_confirmation.html" % app_label,
104 | "admin/delete_selected_confirmation.html",
105 | ],
106 | context,
107 | context_instance=template.RequestContext(request),
108 | )
109 |
110 |
111 | delete_selected.short_description = _("Delete selected %(verbose_name_plural)s")
112 |
--------------------------------------------------------------------------------
/django_mongoengine/mongo_admin/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 | from django.core import checks
3 | from django.utils.translation import gettext_lazy as _
4 |
5 |
6 | def check_admin_app(**kwargs):
7 | from .sites import system_check_errors
8 |
9 | return system_check_errors
10 |
11 |
12 | class SimpleMongoAdminConfig(AppConfig):
13 | """Simple AppConfig which does not do automatic discovery."""
14 |
15 | name = "django_mongoengine.mongo_admin"
16 | verbose_name = _("Administration")
17 |
18 | def ready(self):
19 | checks.register(check_admin_app, checks.Tags.admin)
20 |
21 |
22 | class MongoAdminConfig(SimpleMongoAdminConfig):
23 | def ready(self):
24 | super().ready()
25 | self.module.autodiscover()
26 |
--------------------------------------------------------------------------------
/django_mongoengine/mongo_admin/decorators.py:
--------------------------------------------------------------------------------
1 | def register(*models, **kwargs):
2 | """
3 | Registers the given model(s) classes and wrapped ModelAdmin class with
4 | admin site:
5 |
6 | @register(Author)
7 | class AuthorAdmin(admin.ModelAdmin):
8 | pass
9 |
10 | A kwarg of `site` can be passed as the admin site, otherwise the default
11 | admin site will be used.
12 | """
13 | from .options import DocumentAdmin
14 | from .sites import AdminSite, site
15 |
16 | def _model_admin_wrapper(admin_class):
17 | admin_site = kwargs.pop("site", site)
18 |
19 | if not isinstance(admin_site, AdminSite):
20 | raise ValueError("site must subclass AdminSite")
21 |
22 | if not issubclass(admin_class, DocumentAdmin):
23 | raise ValueError("Wrapped class must subclass ModelAdmin.")
24 |
25 | admin_site.register(models, admin_class=admin_class)
26 |
27 | return admin_class
28 |
29 | return _model_admin_wrapper
30 |
--------------------------------------------------------------------------------
/django_mongoengine/mongo_admin/helpers.py:
--------------------------------------------------------------------------------
1 | from django.contrib.admin.helpers import AdminField
2 | from django.contrib.admin.helpers import AdminForm as DjangoAdminForm
3 | from django.contrib.admin.helpers import AdminReadonlyField as DjangoAdminReadonlyField
4 | from django.contrib.admin.helpers import Fieldline as DjangoFieldLine
5 | from django.contrib.admin.helpers import Fieldset as DjangoFieldSet
6 | from django.contrib.admin.helpers import InlineAdminForm as DjangoInlineAdminForm
7 | from django.contrib.admin.helpers import InlineAdminFormSet as DjangoInlineAdminFormSet
8 | from django.contrib.admin.helpers import InlineFieldset as DjangoInlineFieldset
9 | from django.contrib.admin.utils import lookup_field
10 | from django.core.exceptions import ObjectDoesNotExist
11 | from django.utils.encoding import smart_str
12 | from django.utils.html import conditional_escape
13 | from django.utils.safestring import mark_safe
14 |
15 | from django_mongoengine.mongo_admin.util import (
16 | display_for_field,
17 | help_text_for_field,
18 | label_for_field,
19 | )
20 |
21 |
22 | class AdminForm(DjangoAdminForm):
23 | def __iter__(self):
24 | for name, options in self.fieldsets:
25 | yield Fieldset(
26 | self.form,
27 | name,
28 | readonly_fields=self.readonly_fields,
29 | model_admin=self.model_admin,
30 | **options,
31 | )
32 |
33 |
34 | class Fieldset(DjangoFieldSet):
35 | def __iter__(self):
36 | for field in self.fields:
37 | yield Fieldline(self.form, field, self.readonly_fields, model_admin=self.model_admin)
38 |
39 |
40 | class Fieldline(DjangoFieldLine):
41 | def __iter__(self):
42 | for i, field in enumerate(self.fields):
43 | if field in self.readonly_fields:
44 | yield AdminReadonlyField(
45 | self.form, field, is_first=(i == 0), model_admin=self.model_admin
46 | )
47 | else:
48 | yield AdminField(self.form, field, is_first=(i == 0))
49 |
50 |
51 | class AdminReadonlyField(DjangoAdminReadonlyField):
52 | def __init__(self, form, field, is_first, model_admin=None):
53 | label = label_for_field(field, form._meta.model, model_admin)
54 | # Make self.field look a little bit like a field. This means that
55 | # {{ field.name }} must be a useful class name to identify the field.
56 | # For convenience, store other field-related data here too.
57 | if callable(field):
58 | class_name = field.__name__ != "" and field.__name__ or ""
59 | else:
60 | class_name = field
61 | self.field = {
62 | "name": class_name,
63 | "label": label,
64 | "field": field,
65 | "help_text": help_text_for_field(class_name, form._meta.model),
66 | }
67 | self.form = form
68 | self.model_admin = model_admin
69 | self.is_first = is_first
70 | self.is_checkbox = False
71 | self.is_readonly = True
72 |
73 | def contents(self):
74 | from django.contrib.admin.templatetags.admin_list import _boolean_icon
75 |
76 | field, obj, model_admin = self.field["field"], self.form.instance, self.model_admin
77 | try:
78 | f, attr, value = lookup_field(field, obj, model_admin)
79 | except (AttributeError, ValueError, ObjectDoesNotExist):
80 | result_repr = self.model_admin.get_empty_value_diplay()
81 | else:
82 | if f is None:
83 | boolean = getattr(attr, "boolean", False)
84 | if boolean:
85 | result_repr = _boolean_icon(value)
86 | else:
87 | result_repr = smart_str(value)
88 | if getattr(attr, "allow_tags", False):
89 | result_repr = mark_safe(result_repr)
90 | else:
91 | if value is None:
92 | result_repr = self.model_admin.get_empty_value_diplay()
93 | # HERE WE NEED TO CHANGE THIS TEST
94 | # elif isinstance(f.rel, ManyToManyRel):
95 | # result_repr = ", ".join(map(unicode, value.all()))
96 | else:
97 | result_repr = display_for_field(value, f)
98 | return conditional_escape(result_repr)
99 |
100 |
101 | class InlineAdminFormSet(DjangoInlineAdminFormSet):
102 | """
103 | A wrapper around an inline formset for use in the admin system.
104 | """
105 |
106 | def __iter__(self):
107 | for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()):
108 | yield InlineAdminForm(
109 | self.formset,
110 | form,
111 | self.fieldsets,
112 | self.opts.prepopulated_fields,
113 | original,
114 | self.readonly_fields,
115 | model_admin=self.opts,
116 | )
117 | for form in self.formset.extra_forms:
118 | yield InlineAdminForm(
119 | self.formset,
120 | form,
121 | self.fieldsets,
122 | self.opts.prepopulated_fields,
123 | None,
124 | self.readonly_fields,
125 | model_admin=self.opts,
126 | )
127 | yield InlineAdminForm(
128 | self.formset,
129 | self.formset.empty_form,
130 | self.fieldsets,
131 | self.opts.prepopulated_fields,
132 | None,
133 | self.readonly_fields,
134 | model_admin=self.opts,
135 | )
136 |
137 |
138 | class InlineAdminForm(DjangoInlineAdminForm, AdminForm):
139 | """
140 | A wrapper around an inline form for use in the admin system.
141 | """
142 |
143 | def __init__(
144 | self,
145 | formset,
146 | form,
147 | fieldsets,
148 | prepopulated_fields,
149 | original,
150 | readonly_fields=None,
151 | model_admin=None,
152 | ):
153 | self.formset = formset
154 | self.model_admin = model_admin
155 | self.original = original
156 | self.show_url = original and hasattr(original, "get_absolute_url")
157 | AdminForm.__init__(self, form, fieldsets, prepopulated_fields, readonly_fields, model_admin)
158 |
159 | def pk_field(self):
160 | # if there is no pk field then it's an embedded form so return none
161 | if hasattr(self.formset, "_pk_field"):
162 | return DjangoInlineAdminForm.pk_field(self)
163 | else:
164 | return None
165 |
166 | def __iter__(self):
167 | for name, options in self.fieldsets:
168 | yield InlineFieldset(
169 | self.formset,
170 | self.form,
171 | name,
172 | self.readonly_fields,
173 | model_admin=self.model_admin,
174 | **options,
175 | )
176 |
177 |
178 | class InlineFieldset(DjangoInlineFieldset):
179 | def __iter__(self):
180 | fk = getattr(self.formset, "fk", None)
181 | for field in self.fields:
182 | if fk and fk.name == field:
183 | continue
184 | yield Fieldline(self.form, field, self.readonly_fields, model_admin=self.model_admin)
185 |
--------------------------------------------------------------------------------
/django_mongoengine/mongo_admin/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MongoEngine/django-mongoengine/298cdcf59b21fe590958f57f27bf28e7192fcc4e/django_mongoengine/mongo_admin/management/__init__.py
--------------------------------------------------------------------------------
/django_mongoengine/mongo_admin/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MongoEngine/django-mongoengine/298cdcf59b21fe590958f57f27bf28e7192fcc4e/django_mongoengine/mongo_admin/management/commands/__init__.py
--------------------------------------------------------------------------------
/django_mongoengine/mongo_admin/management/commands/createmongodbsuperuser.py:
--------------------------------------------------------------------------------
1 | """
2 | Management utility to create superusers.
3 | """
4 |
5 | import getpass
6 | import re
7 | import sys
8 |
9 | from django.core import exceptions
10 | from django.core.management.base import BaseCommand, CommandError
11 | from django.utils.translation import gettext as _
12 |
13 | from django_mongoengine.mongo_auth.models import MongoUser
14 | from django_mongoengine.sessions import DEFAULT_CONNECTION_NAME
15 |
16 |
17 | def get_default_username():
18 | return "admin"
19 |
20 |
21 | RE_VALID_USERNAME = re.compile(r"[\w.@+-]+$")
22 |
23 | EMAIL_RE = re.compile(
24 | r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom
25 | r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"' # quoted-string
26 | r")@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$",
27 | re.IGNORECASE,
28 | ) # domain
29 |
30 |
31 | def is_valid_email(value):
32 | if not EMAIL_RE.search(value):
33 | raise exceptions.ValidationError(_("Enter a valid e-mail address."))
34 |
35 |
36 | class Command(BaseCommand):
37 | help = "Used to create a superuser."
38 |
39 | def add_arguments(self, parser):
40 | parser.add_argument(
41 | "--username",
42 | dest="username",
43 | default=None,
44 | help="Specifies the username for the superuser.",
45 | )
46 | parser.add_argument(
47 | "--email",
48 | dest="email",
49 | default=None,
50 | help="Specifies the email address for the superuser.",
51 | )
52 | parser.add_argument(
53 | "--noinput",
54 | action="store_false",
55 | dest="interactive",
56 | default=True,
57 | help=(
58 | "Tells Django to NOT prompt the user for input of any kind. "
59 | "You must use --username and --email with --noinput, and "
60 | "superusers created with --noinput will not be able to log "
61 | "in until they're given a valid password."
62 | ),
63 | )
64 | parser.add_argument(
65 | "--database",
66 | action="store",
67 | dest="database",
68 | default=DEFAULT_CONNECTION_NAME,
69 | help='Specifies the database to use. Default is "default".',
70 | )
71 |
72 | def handle(self, *args, **options):
73 | username = options.get("username", None)
74 | email = options.get("email", None)
75 | interactive = options.get("interactive")
76 | verbosity = int(options.get("verbosity", 1))
77 |
78 | # Do quick and dirty validation if --noinput
79 | if not interactive:
80 | if not username or not email:
81 | raise CommandError("You must use --username and --email with --noinput.")
82 | if not RE_VALID_USERNAME.match(username):
83 | raise CommandError("Invalid username. Use only letters, digits, and underscores")
84 | try:
85 | is_valid_email(email)
86 | except exceptions.ValidationError:
87 | raise CommandError("Invalid email address.")
88 |
89 | # If not provided, create the user with an unusable password
90 | password = None
91 |
92 | # Prompt for username/email/password. Enclose this whole thing in a
93 | # try/except to trap for a keyboard interrupt and exit gracefully.
94 | if interactive:
95 | default_username = get_default_username()
96 | try:
97 | # Get a username
98 | while 1:
99 | if not username:
100 | input_msg = "Username"
101 | if default_username:
102 | input_msg += " (leave blank to use %r)" % default_username
103 | username = input(input_msg + ": ")
104 | if default_username and username == "":
105 | username = default_username
106 | if not RE_VALID_USERNAME.match(username):
107 | sys.stderr.write(
108 | "Error: That username is invalid. Use only letters, digits and underscores.\n"
109 | )
110 | username = None
111 | continue
112 | try:
113 | MongoUser.objects.get(username=username)
114 | except MongoUser.DoesNotExist:
115 | break
116 | else:
117 | sys.stderr.write("Error: That username is already taken.\n")
118 | username = None
119 |
120 | # Get an email
121 | while 1:
122 | if not email:
123 | email = input("E-mail address: ")
124 | try:
125 | is_valid_email(email)
126 | except exceptions.ValidationError:
127 | sys.stderr.write("Error: That e-mail address is invalid.\n")
128 | email = None
129 | else:
130 | break
131 |
132 | # Get a password
133 | while 1:
134 | if not password:
135 | password = getpass.getpass()
136 | password2 = getpass.getpass("Password (again): ")
137 | if password != password2:
138 | sys.stderr.write("Error: Your passwords didn't match.\n")
139 | password = None
140 | continue
141 | if password.strip() == "":
142 | sys.stderr.write("Error: Blank passwords aren't allowed.\n")
143 | password = None
144 | continue
145 | break
146 | except KeyboardInterrupt:
147 | sys.stderr.write("\nOperation cancelled.\n")
148 | sys.exit(1)
149 |
150 | MongoUser.objects.create_superuser(username, email, password)
151 | if verbosity >= 1:
152 | self.stdout.write("Superuser created successfully.\n")
153 |
--------------------------------------------------------------------------------
/django_mongoengine/mongo_admin/sites.py:
--------------------------------------------------------------------------------
1 | from django.contrib.admin import sites
2 | from mongoengine.base import TopLevelDocumentMetaclass
3 |
4 | from django_mongoengine.forms.document_options import DocumentMetaWrapper
5 | from django_mongoengine.mongo_admin.options import DocumentAdmin
6 |
7 | # from django_mongoengine.mongo_admin import actions
8 |
9 | system_check_errors = []
10 |
11 |
12 | class AdminSite(sites.AdminSite):
13 | index_template = "mongo_admin/index.html"
14 |
15 | def register(self, model_or_iterable, admin_class=None, **options):
16 | if isinstance(model_or_iterable, TopLevelDocumentMetaclass) and not admin_class:
17 | admin_class = DocumentAdmin
18 |
19 | if isinstance(model_or_iterable, TopLevelDocumentMetaclass):
20 | model_or_iterable._meta = DocumentMetaWrapper(model_or_iterable)
21 | model_or_iterable = [model_or_iterable]
22 |
23 | super().register(model_or_iterable, admin_class, **options)
24 |
25 | def unregister(self, model_or_iterable):
26 | if isinstance(model_or_iterable, TopLevelDocumentMetaclass):
27 | model_or_iterable = [model_or_iterable]
28 |
29 | super().unregister(model_or_iterable)
30 |
31 |
32 | # This global object represents the default admin site, for the common case.
33 | # You can instantiate AdminSite in your own code to create a custom admin site.
34 | site = AdminSite(name="mongo_admin")
35 |
--------------------------------------------------------------------------------
/django_mongoengine/mongo_admin/static/helper.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function(){
2 |
3 | /*----------------------------------------------------------------------*/
4 | var DEBUG = false;
5 |
6 | function print_array(a,name){
7 | debug('--------------------');
8 | if ($.isArray(a)){
9 | for (var key in a){
10 | if (key === 'length' || !a.hasOwnProperty(key)) continue;
11 | debug(name+'['+key+'] = '+a[key]);
12 | }
13 | }
14 | }
15 | /*----------------------------------------------------------------------*/
16 |
17 | /************************************************************************
18 | Transform full read-only dictionaries in text
19 | ************************************************************************/
20 |
21 | var readonly_dicts = $('p:contains({)');
22 | debug(readonly_dicts);
23 | readonly_dicts.each(function(){
24 | var text = $(this).text();
25 | text = text.replace(/u'/g, '');
26 | text = text.replace(/'/g, '');
27 | $(this).html(get_html(text));
28 | });
29 |
30 | function get_html(text){
31 | debug('------ENTERING get_html');
32 | var html = '
';
33 | var pairs = get_pairs(text);
34 | debug(pairs);
35 | for (var i = 0; i < pairs.length; i++){
36 | if (pairs[i][1].contains('{')){
37 | html += '