MongoDB Schema Object - A ODM from Mongo DB JSON validation schema
Project description
MSO (Mongo Schema Object Library)
MSO is a lightweight Object-Document Mapper (ODM) for MongoDB that allows Python developers to interact with MongoDB collections in an intuitive and Pythonic way. It offers the flexibility of a schema-less database with the convenience of strongly-typed classes, enabling seamless operations on MongoDB collections using familiar Python patterns.
๐ Key Features:
- Dynamic Model Generation: Automatically generates Python classes from your MongoDB collectionโs
$jsonSchema. - Pythonic API: Use common patterns like
save(),find_one(),update_one(), etc. - REST API with Swagger: Automatically generates a REST API for your models with Swagger documentation.
- Deeply Nested Models: Supports arbitrarily nested schemas, including arrays of objects.
- Auto-validation: Ensures types, enums, and structure match your schema.
- Recursive Object Serialization: Works out-of-the-box with nested documents and arrays.
- Developer Tools: Includes tree views, schema printers, and class introspection.
๐ฆ Requirements
- Python 3.12+
- MongoDB with
$jsonSchemavalidation on your collections
๐ง Installation
pip install mso
Recommended MongoDB Validation Schema Format
{
$jsonSchema: {
bsonType: 'object',
properties: {
_id: {
bsonType: 'objectId'
},
# ADD YOUR FIELDS HERE
last_modified: {
bsonType: [
'date',
'null'
]
},
created_at: {
bsonType: [
'date',
'null'
]
}
},
additionalProperties: false
}
}
๐ ๏ธ Basic Usage
In this basic example we have already created a $jsonSchema validator for the "People" collection in MongoDB. We create a new person, update some information and save the person MongoDB.
from mso import connect_to_mongo, get_model
# Connect to MongoDB
db = connect_to_mongo("mongodb://localhost:27017", "db-name")
# Generate a model based on the "people" collection's schema
People = get_model(db, "people")
# Create a new person
person = People(name="Tony Pajama", age=34)
# Add nested data
person.health.primary_physician.name = "Dr. Strange"
person.address.add(type="home", street="123 Elm", city="NY", state="NY", zip="10001")
# Save to the database
person.save()
๐งช View Your Class Tree
People.print_nested_class_tree()
Output
Tree View:
โโโ people
โโโ name: str
โโโ age: int
โโโ email: str
โโโ gender: enum [Male, Female, Other]
โโโ addresses: List[addresses_item]
โ โโโ type: enum [Home, Business, Other]
โ โโโ street: str
โ โโโ city: str
โ โโโ state: str
โ โโโ zip: str
โโโ health: Object
โโโ medical_history: Object
โ โโโ conditions: List[conditions_item]
โ โ โโโ name: str
โ โ โโโ diagnosed: str
โ โ โโโ medications: List[medications_item]
โ โ โโโ name: str
โ โ โโโ dose: str
โ โ โโโ frequency: str
โ โโโ allergies: List
โโโ primary_physician: Object
โโโ name: str
โโโ contact: Object
โโโ phone: str
โโโ address: Object
โโโ street: str
โโโ city: str
โโโ state: str
โโโ zip: str
๐ Querying the Database
# Find one
person = People.find_one({"name": "Tony Pajama"})
# Find many
person_list = People.find_many(sort=[("created_at", -1)], limit=10)
Document Manipulation
# Delete
person.delete()
# Clone
new_person = person.clone()
๐ Data Summary & Analysis
MSO includes a powerful .summarize() method to help you quickly explore and understand your MongoDB collection. It performs a field-level summary with support for:
โ๏ธ Options
sample_size: Limit the number of documents to analyze (defaults to all)
top: Number of top strings to return (default: 5)
๐ Example
from mso import connect_to_mongo, get_model
# Connect to MongoDB
db = connect_to_mongo("mongodb://localhost:27017", "db-name")
# Get the model for the "people" collection
People = get_model(db, "people")
print(People.summarize(top=10))
๐ง Example Output
{
"sample_size": 1000,
"fields": {
"name": {
"type": "str",
"count": 1000,
"missing": 0,
"unique": 993,
"top_5": [
{
"value": "Tony Pajama",
"count": 7,
"percent": 0.007
},
...
]
},
"age": {
"type": "int",
"count": 978,
"missing": 22,
"unique": 43,
"min": 1,
"max": 99,
"mean": 38.6,
"median": 34,
"stdev": 19.2
},
"health.primary_physician.name": {
"type": "str",
"count": 1000,
"missing": 0,
"unique": 12,
"top_5": [
{
"value": "Dr. Strange",
"count": 46,
"percent": 0.046
},
...
]
}
}
}
๐ Document Comparison
MSO makes it easy to compare two MongoDB documentsโeither as model instances or dictionariesโusing the powerful Model.diff() method. It supports:
- Deep recursive comparison of nested objects and arrays
- Detection of value and type changes
- Flat or nested output formatting
- Optional strict mode (type-sensitive)
- Filtering for specific fields or changes
Basic Example
from mso import connect_to_mongo, get_model
db = connect_to_mongo("mongodb://localhost:27017", "db-name")
People = get_model(db, "people")
# Create a valid model instance
person1 = People(name="Alice", age=30, gender="Female")
# Use a dictionary with type mismatch (age as string)
person2 = {
"name": "Alice",
"age": "30", # string instead of int
"gender": "Female"
}
diff = People.diff(person1, person2, strict=True)
from pprint import pprint
pprint(diff)
Example Output
{
'age': {
'old': 30,
'new': '30',
'type_changed': True
}
}
Convert to and from dictionary
person_dict = person.to_dict()
โฑ Automatic Timestamps
By default, models automatically include created_at and updated_at fields to track when a document is created or modified. These are managed internally and do not need to be defined in your schema.
๐ง How it works
created_at is set once, when the document is first saved.
updated_at is updated every time the document is modified and saved.
Both are stored as UTC datetime.datetime objects.
๐ซ Disabling timestamps
Timestamps are enabled by default. To disable them, set the timestamps parameter to False when creating a model.
from mso import connect_to_mongo, get_model
# Connect to MongoDB
db = connect_to_mongo("mongodb://localhost:27017", "db-name")
# Get the model for the collection
People = get_model(db, "people")
# Disable timestamps for a specific model or instance
People.timestamps_enabled = False
๐งฉ Lifecycle Hooks
You can use decorators like @pre_save, @post_save, @pre_delete, and @post_delete to hook into model lifecycle events. This is useful for setting defaults, cleaning up, triggering logs, or validating conditions.
Example: Automatically output a message when a document is saved
from mso import connect_to_mongo, get_model
from mso.base_model import MongoModel, post_save
# Connect to MongoDB
db = connect_to_mongo("mongodb://localhost:27017", "db-name")
# Define the model hooks you would like to use
class People(MongoModel):
@post_save # This method will be called after the document is saved
def confirm_save(self):
print(f"[+] Document saved: {self.name}")
People = get_model(db, "people")
person = People(name="Jane Doe")
person.save()
# Output:
# [+] Document saved: Jane Doe
๐งช REST API with Swagger
MSO can automatically generate a REST API for your models, complete with Swagger documentation. This allows you to easily expose your MongoDB collections as RESTful endpoints.
from mso import connect_to_mongo
from mso.api import start_api
# Connect to MongoDB
db = connect_to_mongo("mongodb://localhost:27017", "db-name")
start_api(db)
This will start a REST API server with Swagger documentation at http://127.0.0.1:8000/docs
๐ Custom API Endpoints
You can extend the auto-generated API with your own custom routes using the extra_routes parameter in start_api.
โ Example
from fastapi import APIRouter
from mso import connect_to_mongo
from mso.api import start_api
# Connect to MongoDB
db = connect_to_mongo("mongodb://localhost:27017", "db-name")
# Define your custom routes
custom_router = APIRouter()
@custom_router.get("/people/stats", tags=["People"])
def get_people_stats():
return {"message": "Custom stats for the People collection"}
# Start the API with custom routes
start_api(
db=db,
collections=["*"],
extra_routes=custom_router
)
๐ Community & Links
PyPi: https://pypi.org/project/MSO/
Reddit: https://www.reddit.com/r/MSO_Mongo_Python_ORM/
Gitlab: https://github.com/chuckbeyor101/MSO-Mongo-Schema-Object-Library.git
๐ก LICENSE & COPYWRIGHT WARNING
MSO Copyright (c) 2025 by Charles L Beyor
is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International.
To view a copy of this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mso-1.0.82.tar.gz.
File metadata
- Download URL: mso-1.0.82.tar.gz
- Upload date:
- Size: 28.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cee188b29774e4a294cbbbd182f144c3ecf91bd6add20de4e77c0f527d27aab2
|
|
| MD5 |
443e1b2fea522de4b140f885c397174b
|
|
| BLAKE2b-256 |
63968855ecd18e3db4f376bc37e91e20417445364c36fa5e4bec48e48c93eb0f
|
Provenance
The following attestation bundles were made for mso-1.0.82.tar.gz:
Publisher:
python-publish.yml on Beyotek/MSO-Mongo-Schema-Object-Library
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mso-1.0.82.tar.gz -
Subject digest:
cee188b29774e4a294cbbbd182f144c3ecf91bd6add20de4e77c0f527d27aab2 - Sigstore transparency entry: 748002738
- Sigstore integration time:
-
Permalink:
Beyotek/MSO-Mongo-Schema-Object-Library@28ca4df333cc4e35eb6511651205931a0910cf13 -
Branch / Tag:
refs/tags/1.0.82 - Owner: https://github.com/Beyotek
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@28ca4df333cc4e35eb6511651205931a0910cf13 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mso-1.0.82-py3-none-any.whl.
File metadata
- Download URL: mso-1.0.82-py3-none-any.whl
- Upload date:
- Size: 33.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1cdbdafd45a626f4c980aa91e5c108fd90c156a1e00bb61659cd39e8b9ac3ce0
|
|
| MD5 |
81a46e942cf79818a0ddc32df51b999a
|
|
| BLAKE2b-256 |
beb0d4c753848c904f8f0121911fc599f4a9e17c13a480afbc9117963545ec0b
|
Provenance
The following attestation bundles were made for mso-1.0.82-py3-none-any.whl:
Publisher:
python-publish.yml on Beyotek/MSO-Mongo-Schema-Object-Library
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mso-1.0.82-py3-none-any.whl -
Subject digest:
1cdbdafd45a626f4c980aa91e5c108fd90c156a1e00bb61659cd39e8b9ac3ce0 - Sigstore transparency entry: 748002739
- Sigstore integration time:
-
Permalink:
Beyotek/MSO-Mongo-Schema-Object-Library@28ca4df333cc4e35eb6511651205931a0910cf13 -
Branch / Tag:
refs/tags/1.0.82 - Owner: https://github.com/Beyotek
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@28ca4df333cc4e35eb6511651205931a0910cf13 -
Trigger Event:
push
-
Statement type: