-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocument_references.py
More file actions
129 lines (100 loc) · 4.43 KB
/
document_references.py
File metadata and controls
129 lines (100 loc) · 4.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
"""
Document References
===================
Demonstrates transparent typed document references.
Any field typed as a MongoDocument subclass is automatically treated as a
reference — stored as plain ObjectId in MongoDB and resolved on read.
"""
import asyncio
import mongojet
import mongospec
from mongospec import MongoDocument
# ---------------------------------------------------------------------------
# Models
# ---------------------------------------------------------------------------
class User(MongoDocument):
__collection_name__ = "users"
name: str
email: str
class Tag(MongoDocument):
__collection_name__ = "tags"
label: str
class Category(MongoDocument):
__collection_name__ = "categories"
title: str
parent: "Category | None" = None # self-referencing
class Post(MongoDocument):
__collection_name__ = "posts"
title: str
author: User # required reference
reviewer: User | None = None # optional reference
tags: list[Tag] = [] # list of references
async def main():
client = await mongojet.create_client("mongodb://localhost:27017")
db = client.get_database("example_refs")
try:
await mongospec.init(db, document_types=[User, Tag, Category, Post])
# --- Create referenced documents ---
alice = User(name="Alice", email="alice@example.com")
bob = User(name="Bob", email="bob@example.com")
await User.insert_many([alice, bob])
python_tag = Tag(label="python")
async_tag = Tag(label="async")
mongo_tag = Tag(label="mongodb")
await Tag.insert_many([python_tag, async_tag, mongo_tag])
# --- Create a post with references (pass real objects) ---
post = Post(
title="Getting Started with mongospec",
author=alice,
reviewer=bob,
tags=[python_tag, async_tag, mongo_tag],
)
await post.insert()
print(f"Created post: {post.title}")
# --- Fetch from DB — references auto-resolved ---
found = await Post.find_one({"title": post.title})
print(f"\nLoaded post from DB: {found.title}")
print(f" author: {found.author.name} ({found.author.email})")
print(f" reviewer: {found.reviewer.name}")
print(f" tags: {[t.label for t in found.tags]}")
# --- Without resolution (performance) ---
raw = await Post.find_one({"title": post.title}, resolve_refs=False)
print(f"\nWithout resolution:")
print(f" author._id: {raw.author._id}") # ObjectId available
print(f" author.name: '{raw.author.name}'") # zero-value (empty string)
# --- Batch resolve (minimal queries) ---
for i in range(5):
await Post(title=f"Extra-{i}", author=alice, tags=[python_tag]).insert()
posts = await Post.find_all({})
print(f"\nBatch loaded {len(posts)} posts — all refs auto-resolved:")
for p in posts:
print(f" {p.title} by {p.author.name}, tags: {[t.label for t in p.tags]}")
# --- Cascading save ---
found = await Post.find_one({"title": post.title})
found.author.name = "Alice Updated"
await found.save() # save_refs=True by default — saves post AND author
print(f"\nAfter cascading save:")
db_author = await User.find_by_id(alice._id)
print(f" Author in DB: {db_author.name}") # "Alice Updated"
# Disable cascade
found.author.name = "Should Not Change"
await found.save(save_refs=False) # only saves the post
db_author2 = await User.find_by_id(alice._id)
print(f" After save_refs=False: {db_author2.name}") # still "Alice Updated"
# --- Nested references (self-referencing) ---
root = Category(title="Technology")
await root.insert()
databases = Category(title="Databases", parent=root)
await databases.insert()
nosql = Category(title="NoSQL", parent=databases)
await nosql.insert()
print(f"\nNested categories:")
leaf = await Category.find_one({"title": "NoSQL"})
print(f" {leaf.title} → {leaf.parent.title} → {leaf.parent.parent.title}")
# --- Query by reference field (just use ObjectId) ---
alice_posts = await Post.find_all({"author": alice._id})
print(f"\nPosts by Alice: {[p.title for p in alice_posts]}")
finally:
await db.drop()
await mongospec.close()
asyncio.run(main())