diff --git a/.github/Taskfile.yml b/.github/Taskfile.yml
new file mode 100644
index 00000000..9dc1d0c7
--- /dev/null
+++ b/.github/Taskfile.yml
@@ -0,0 +1,46 @@
+version: "3"
+
+silent: false
+
+vars:
+ VERSION_FILE: ./__version__.txt
+ RELEASE_NOTE_FILE: ./__release_notes__.md
+ RELEASE_BRANCH: "master"
+ CURRENT_BRANCH:
+ sh: git rev-parse --symbolic-full-name --abbrev-ref HEAD
+ RELEASE_COMMIT_FILES: "pyproject.toml CHANGELOG.md"
+
+tasks:
+ default:
+ preconditions:
+ - sh: which hub
+ msg: hub not found
+ - sh: "[ {{.CURRENT_BRANCH}} = {{.RELEASE_BRANCH}} ]"
+ msg: "Please switch to {{.RELEASE_BRANCH}} to create a release"
+ cmds:
+ - task: prepare-workspace
+ - task: publish-release
+ - task: clean
+
+ prepare-workspace:
+ cmds:
+ - poetry run python .github/release.py
+ publish-release:
+ vars:
+ RELEASE_NOTE:
+ sh: cat {{.RELEASE_NOTE_FILE}}
+ NEW_VERSION:
+ sh: cat {{.VERSION_FILE}}
+ cmds:
+ - git add {{.RELEASE_COMMIT_FILES}}
+ - git commit -m "Release {{.NEW_VERSION}} 🚀"
+ - git push
+ - git tag v{{.NEW_VERSION}}
+ - git push --tags
+ - hub release create -d -t {{.RELEASE_BRANCH}} -m v{{.NEW_VERSION}} -m "$(cat {{.RELEASE_NOTE_FILE}} )" v{{.NEW_VERSION}}
+ - poetry publish --build
+
+ clean:
+ cmds:
+ - rm -f {{.VERSION_FILE}}
+ - rm -f {{.RELEASE_NOTE_FILE}}
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..0d366967
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,11 @@
+version: 2
+updates:
+ - package-ecosystem: pip
+ directory: "/"
+ schedule:
+ interval: daily
+ time: "04:00"
+
+ open-pull-requests-limit: 10
+ commit-message:
+ prefix: "⬆️"
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 42ebf5b6..0d44b2fa 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -3,6 +3,7 @@ on:
release:
types:
- published
+ - released
- edited
workflow_dispatch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fb58a9c0..177ad8d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [0.3.1] - 2020-11-16
+
+#### Added
+
+- Add `schema_extra` config option ([#41](https://github.com/art049/odmantic/pull/41) by [@art049](https://github.com/art049))
+
+#### Fixed
+
+- Fix `setattr` error on a manually initialized EmbeddedModel ([#40](https://github.com/art049/odmantic/pull/40) by [@art049](https://github.com/art049))
+
## [0.3.0] - 2020-11-09
#### Deprecated
@@ -15,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
#### Added
-- Allow parsing document with unset fields defaults ([#28](https://github.com/art049/odmantic/pull/28) by [@art049](https://github.com/art049))
+- Allow parsing document with unset fields defaults ([documentation](https://art049.github.io/odmantic/raw_query_usage/#advanced-parsing-behavior)) ([#28](https://github.com/art049/odmantic/pull/28) by [@art049](https://github.com/art049))
- Integration with Pydantic `Config` class ([#37](https://github.com/art049/odmantic/pull/37) by [@art049](https://github.com/art049)):
@@ -66,4 +76,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[0.2.0]: https://github.com/art049/odmantic/compare/v0.1.0...v0.2.0
[0.2.1]: https://github.com/art049/odmantic/compare/v0.2.0...v0.2.1
[0.3.0]: https://github.com/art049/odmantic/compare/v0.2.1...v0.3.0
-[unreleased]: https://github.com/art049/odmantic/compare/v0.3.0...HEAD
+[0.3.1]: https://github.com/art049/odmantic/compare/v0.3.0...v0.3.1
+[unreleased]: https://github.com/art049/odmantic/compare/v0.3.1...HEAD
diff --git a/README.md b/README.md
index 03ab8819..9f128a60 100644
--- a/README.md
+++ b/README.md
@@ -22,9 +22,10 @@
---
-ODMantic is an Object Document Mapper (a kind of ORM but for NoSQL databases) for
-MongoDB based on standard python type hints. It's built on top of
-pydantic for model definition and validation.
+Asynchronous ODM(Object Document Mapper) for MongoDB based on standard python type hints. It's built on top of pydantic for model
+definition and validation.
Core features:
@@ -32,12 +33,14 @@ Core features:
using python comparison operators
- **Developer experience**: field/method autocompletion, type hints, data validation,
- perform database operations in a functional way
+ perform database operations with a functional API
- **Fully typed**: leverage static analysis to reduce runtime issues
- **AsyncIO**: works well with ASGI frameworks (FastAPI, FastAPI, quart, sanic, Starlette, ...)
- **Serialization**: built in JSON serialization and JSON schema generation
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index 65ef752e..7e828d98 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -2,7 +2,7 @@ site_name: ODMantic
site_description: AsyncIO MongoDB ODM (Object Document Mapper) using python type hinting
repo_name: art049/odmantic
repo_url: https://github.com/art049/odmantic
-
+site_url: https://art049.github.io/odmantic/
strict: true
docs_dir: .
site_dir: ../site
diff --git a/docs/modeling.md b/docs/modeling.md
index 8c387a16..ce15c717 100644
--- a/docs/modeling.md
+++ b/docs/modeling.md
@@ -80,6 +80,12 @@ in the model body.
Default: name of the model class
+`#!python schema_extra: dict` *(inherited from Pydantic)*
+ : A dict used to extend/update the generated JSON Schema, or a callable to
+ post-process it. See [Pydantic: Schema customization](https://pydantic-docs.helpmanual.io/usage/schema/#schema-customization){:target=_blank} for more details.
+
+ Default: `#!python {}`
+
`#!python anystr_strip_whitespace: bool` *(inherited from Pydantic)*
: Whether to strip leading and trailing whitespaces for str & byte types.
diff --git a/odmantic/config.py b/odmantic/config.py
index dc311862..17eda168 100644
--- a/odmantic/config.py
+++ b/odmantic/config.py
@@ -1,7 +1,7 @@
import json
-from typing import Any, Callable, Dict, Optional, Type
+from typing import Any, Callable, Dict, Optional, Type, Union
-from pydantic.main import BaseConfig
+from pydantic.main import BaseConfig, SchemaExtraCallable
from pydantic.typing import AnyCallable
from odmantic.bson import BSON_TYPES_ENCODERS
@@ -19,6 +19,7 @@ class BaseODMConfig:
# Inherited from pydantic
title: Optional[str] = None
json_encoders: Dict[Type[Any], AnyCallable] = {}
+ schema_extra: Union[Dict[str, Any], "SchemaExtraCallable"] = {}
anystr_strip_whitespace: bool = False
json_loads: Callable[[str], Any] = json.loads
json_dumps: Callable[..., str] = json.dumps
diff --git a/odmantic/model.py b/odmantic/model.py
index b5a496d9..bb042f1f 100644
--- a/odmantic/model.py
+++ b/odmantic/model.py
@@ -472,6 +472,10 @@ class _BaseODMModel(pydantic.BaseModel, metaclass=ABCMeta):
__slots__ = ("__fields_modified__",)
+ def __init__(self, **data: Any):
+ super().__init__(**data)
+ object.__setattr__(self, "__fields_modified__", set(self.__odm_fields__.keys()))
+
@classmethod
def validate(cls: Type[TBase], value: Any) -> TBase:
if isinstance(value, cls):
@@ -617,16 +621,6 @@ class Model(_BaseODMModel, metaclass=ModelMetaclass):
id: Union[ObjectId, Any] # TODO fix basic id field typing
- def __init__(__odmantic_self__, **data: Any):
- super().__init__(**data)
- # Uses something other than `self` the first arg to allow "self" as a settable
- # attribute
- object.__setattr__(
- __odmantic_self__,
- "__fields_modified__",
- set(__odmantic_self__.__odm_fields__.keys()),
- )
-
def __setattr__(self, name: str, value: Any) -> None:
if name == self.__primary_field__:
# TODO implement
diff --git a/pyproject.toml b/pyproject.toml
index caa34034..45f65787 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "ODMantic"
-version = "0.3.0"
+version = "0.3.1"
description = "ODMantic, an AsyncIO MongoDB Object Document Mapper for Python using type hints "
authors = ["Arthur Pastel "]
license = "ISC License"
@@ -37,11 +37,11 @@ classifiers = [
include = ["LICENSE", "README.md"]
[tool.poetry.dependencies]
-python = ">=3.6 <4.0"
+python = ">=3.6,<4.0"
importlib-metadata = { version = "^1.0", python = "<3.8" }
typing-extensions = { version = "^3.7.4.3", python = "<3.8" }
-pydantic = ">=1.6.0 < 1.8.0"
-motor = ">=2.1.0 <2.4.0"
+pydantic = ">=1.6.0,< 1.8.0"
+motor = ">=2.1.0,<2.4.0"
fastapi = { version = "^0.61.1", optional = true }
[tool.poetry.extras]
diff --git a/tests/unit/test_model_logic.py b/tests/unit/test_model_logic.py
index 1b013899..d897c379 100644
--- a/tests/unit/test_model_logic.py
+++ b/tests/unit/test_model_logic.py
@@ -32,6 +32,14 @@ class M(Model):
assert instance.__fields_modified__ == set(["f", "id"])
+def test_fields_embedded_modified_no_modification():
+ class M(EmbeddedModel):
+ f: int
+
+ instance = M(f=0)
+ assert instance.__fields_modified__ == set(["f"])
+
+
def test_fields_modified_with_default():
class M(Model):
f: int = 5
@@ -40,13 +48,15 @@ class M(Model):
assert instance.__fields_modified__ == set(["f", "id"])
-def test_fields_modified_one_update():
- class M(Model):
+@pytest.mark.parametrize("model_cls", [Model, EmbeddedModel])
+def test_fields_modified_one_update(model_cls):
+ class M(model_cls): # type: ignore
f: int
instance = M(f=0)
+ instance.__fields_modified__.clear()
instance.f = 1
- assert instance.__fields_modified__ == set(["f", "id"])
+ assert instance.__fields_modified__ == set(["f"])
def test_field_update_with_invalid_data_type():