docs: collect refreshed mongo-ops wiki site (API-correct overhaul, v0.1.5)
This commit is contained in:
@@ -389,6 +389,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||||
|
|
||||||
|
|
||||||
@@ -421,6 +423,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__title" for="__toc">
|
<label class="md-nav__title" for="__toc">
|
||||||
<span class="md-nav__icon md-icon"></span>
|
<span class="md-nav__icon md-icon"></span>
|
||||||
Table of contents
|
Table of contents
|
||||||
@@ -428,9 +432,36 @@
|
|||||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#library-overview" class="md-nav__link">
|
<a href="#architecture" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Library Overview
|
🏗️ Architecture
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#lifecycle-rules" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
🔄 Lifecycle Rules
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#which-repository-should-you-use" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
🧭 Which Repository Should You Use?
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#read-next" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
➡️ Read Next
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -501,7 +532,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -998,18 +1029,125 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1>Overview</h1>
|
<h1 id="library-overview">Library Overview</h1>
|
||||||
|
<p><code>mongo-ops</code> gives FastAPI/microservice teams a small, opinionated toolkit for talking to MongoDB asynchronously. This page builds the mental model: what the layers are, in what order they must be wired up, and which repository to reach for.</p>
|
||||||
<h2 id="library-overview">Library Overview</h2>
|
|
||||||
<p><code>mongo-ops</code> is a modular MongoDB operations layer for FastAPI microservices. It provides:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Async connection management</li>
|
|
||||||
<li>Base document models with auto-timestamps</li>
|
|
||||||
<li>Generic CRUD repository pattern</li>
|
|
||||||
<li>Transaction support</li>
|
|
||||||
<li>Model registration system</li>
|
|
||||||
</ul>
|
|
||||||
<hr />
|
<hr />
|
||||||
|
<h2 id="architecture">🏗️ Architecture</h2>
|
||||||
|
<div class="language-text highlight"><span class="filename">Text Only</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a>┌───────────────────────────────────────────────────────────┐
|
||||||
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>│ Application (FastAPI) │
|
||||||
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>│ ┌─────────────┬───────────────┬───────────────┬─────────┐ │
|
||||||
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a>│ │ CachedBase │ Populating │ BaseRepository│ Raw │ │
|
||||||
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a>│ │ Repository │ Repository │ (CRUDMixin) │ Motor │ │
|
||||||
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>│ └──────┬──────┴───────┬───────┴───────┬───────┴─────────┘ │
|
||||||
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a>│ │ │ │ │
|
||||||
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a>│ ┌──────┴──────┐ ┌────┴──────┐ ┌─────┴─────────────────┐ │
|
||||||
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a>│ │ Cache backend│ │ Population│ │ TransactionManager │ │
|
||||||
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a>│ │ memory/redis │ │ Engine │ │ (session helpers) │ │
|
||||||
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a>│ └──────┬──────┘ └────┬──────┘ └─────────┬─────────────┘ │
|
||||||
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a>│ ┌──────┴──────────────────────────────────┴─────────────┐ │
|
||||||
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a>│ │ ModelRegistry (models, indexes, cache lifecycle) │ │
|
||||||
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a>│ └──────┬─────────────────────────────────────────────────┘ │
|
||||||
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a>│ ┌──────┴─────────────────────────────────────────────────┐ │
|
||||||
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a>│ │ MongoConnectionManager (Motor client + database) │ │
|
||||||
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a>│ └──────────────────────────┬──────────────────────────────┘ │
|
||||||
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a>└─────────────────────────────┼────────────────────────────────┘
|
||||||
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> ▼
|
||||||
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> MongoDB (Motor / PyMongo)
|
||||||
|
</code></pre></div>
|
||||||
|
<p>The layers build on each other:</p>
|
||||||
|
<ol>
|
||||||
|
<li><strong>Connection</strong> — <code>MongoConnectionManager</code> owns the Motor client and active database.</li>
|
||||||
|
<li><strong>Models</strong> — <code>BaseDocument</code> (Pydantic v2) gives every document <code>id</code> (aliased <code>_id</code>) plus <code>created_at</code> / <code>updated_at</code>.</li>
|
||||||
|
<li><strong>CRUD</strong> — <code>CRUDMixin</code> / <code>BaseRepository</code> implement generic async CRUD against a collection.</li>
|
||||||
|
<li><strong>Startup</strong> — <code>ModelRegistry</code> centralizes model + index registration and cache lifecycle for multi-collection services.</li>
|
||||||
|
<li><strong>Advanced layers</strong> — caching and population compose <em>around</em> a repository:</li>
|
||||||
|
<li><code>CachedBaseRepository</code> wraps <code>BaseRepository</code> with a <code>CacheBackend</code>.</li>
|
||||||
|
<li><code>PopulatingRepository</code> wraps <code>BaseRepository</code> with a <code>PopulationEngine</code>.</li>
|
||||||
|
<li><strong>Transactions</strong> — <code>TransactionManager</code> runs a list of async operations atomically on a session.</li>
|
||||||
|
</ol>
|
||||||
|
<hr />
|
||||||
|
<h2 id="lifecycle-rules">🔄 Lifecycle Rules</h2>
|
||||||
|
<p><code>mongo-ops</code> has a strict startup order. Violating it raises fast, loud exceptions:</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Step</th>
|
||||||
|
<th>Call</th>
|
||||||
|
<th>Why</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>1</td>
|
||||||
|
<td><code>await MongoConnectionManager.connect(uri, db_name)</code></td>
|
||||||
|
<td>Without a connection, <code>get_database()</code>/<code>get_client()</code> raise <code>RuntimeError("Database not connected...")</code>.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>2</td>
|
||||||
|
<td><code>await ModelRegistry.initialize_all()</code></td>
|
||||||
|
<td>Create registered indexes (idempotent via <code>create_index</code>).</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>3</td>
|
||||||
|
<td><code>await ModelRegistry.set_cache_backend(...)</code> then <code>await ModelRegistry.initialize_cache()</code></td>
|
||||||
|
<td>Must happen <strong>after</strong> connection, <strong>before</strong> any cache-backed operation. Raises <code>RuntimeError("No cache backend registered...")</code> if skipped.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>4</td>
|
||||||
|
<td>Use repositories</td>
|
||||||
|
<td>Constructed repos resolve the collection from the live database.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>5</td>
|
||||||
|
<td><code>await ModelRegistry.shutdown_cache()</code> + <code>await MongoConnectionManager.disconnect()</code></td>
|
||||||
|
<td>On shutdown (in-memory TTL task cancelled; Redis pub/sub closed).</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p>The canonical wiring is the FastAPI <strong>lifespan</strong> context manager (see <a href="../03_use_cases/01_basic_crud/">use case 01</a>).</p>
|
||||||
|
<hr />
|
||||||
|
<h2 id="which-repository-should-you-use">🧭 Which Repository Should You Use?</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Repository</th>
|
||||||
|
<th>Use when</th>
|
||||||
|
<th>Adds</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>BaseRepository[T]</code></td>
|
||||||
|
<td>Plain CRUD — the default</td>
|
||||||
|
<td>nothing extra</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>CachedBaseRepository[T]</code></td>
|
||||||
|
<td>Read‑heavy, low‑write fields (lookups by <code>_id</code>)</td>
|
||||||
|
<td>cache‑first <code>get_by_id</code>, cache on <code>create</code>, invalidate on <code>update</code>/<code>delete</code>, <code>warm_cache</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>PopulatingRepository[T]</code></td>
|
||||||
|
<td>You return related docs (FK references) denormalized</td>
|
||||||
|
<td><code>_populate</code> on read, <code>_depopulate</code> on write, FK‑guarded <code>patch</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>CRUDMixin</code></td>
|
||||||
|
<td>Reusing CRUD inside an existing class</td>
|
||||||
|
<td>raw CRUD against a collection you already have</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<blockquote>
|
||||||
|
<p>There is <strong>no built‑in <code>CachedPopulatingRepository</code></strong>. <a href="../03_use_cases/10_cache_and_population/">Use case 10</a> shows how to compose caching + population in a small subclass.</p>
|
||||||
|
</blockquote>
|
||||||
|
<hr />
|
||||||
|
<h2 id="read-next">➡️ Read Next</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../02_components/">Core Components</a> — the validated public API surface.</li>
|
||||||
|
<li><a href="../03_use_cases/01_basic_crud/">Use case 01</a> — the minimal FastAPI app.</li>
|
||||||
|
<li><a href="../04_best_practices/">Best Practices</a> — team-wide conventions.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<title>02 components - Mongo Ops Documentation</title>
|
<title>Core Components - Mongo Ops Documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
<div class="md-header__topic" data-md-component="header-topic">
|
<div class="md-header__topic" data-md-component="header-topic">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
|
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -459,12 +459,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -477,7 +479,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -491,6 +493,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__title" for="__toc">
|
<label class="md-nav__title" for="__toc">
|
||||||
<span class="md-nav__icon md-icon"></span>
|
<span class="md-nav__icon md-icon"></span>
|
||||||
Table of contents
|
Table of contents
|
||||||
@@ -498,16 +502,6 @@
|
|||||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#core-components" class="md-nav__link">
|
|
||||||
<span class="md-ellipsis">
|
|
||||||
Core Components
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<nav class="md-nav" aria-label="Core Components">
|
|
||||||
<ul class="md-nav__list">
|
|
||||||
|
|
||||||
<li class="md-nav__item">
|
|
||||||
<a href="#1-mongoconnectionmanager" class="md-nav__link">
|
<a href="#1-mongoconnectionmanager" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
1. MongoConnectionManager
|
1. MongoConnectionManager
|
||||||
@@ -516,92 +510,119 @@
|
|||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#2-basedocument" class="md-nav__link">
|
<a href="#2-basedocument-pyobjectid" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
2. BaseDocument
|
2. BaseDocument & PyObjectId
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#3-baserepositoryt" class="md-nav__link">
|
<a href="#3-crudmixint" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
3. BaseRepository[T]
|
3. CRUDMixin[T]
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#4-transactionmanager" class="md-nav__link">
|
<a href="#4-baserepositoryt" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
4. TransactionManager
|
4. BaseRepository[T]
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#5-modelregistry" class="md-nav__link">
|
<a href="#5-populatingrepositoryt" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
5. ModelRegistry
|
5. PopulatingRepository[T]
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#6-cache-backend" class="md-nav__link">
|
<a href="#6-transactionmanager" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
6. Cache Backend
|
6. TransactionManager
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<nav class="md-nav" aria-label="6. Cache Backend">
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#7-modelregistry" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
7. ModelRegistry
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#8-cache-layer" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
8. Cache Layer
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<nav class="md-nav" aria-label="8. Cache Layer">
|
||||||
<ul class="md-nav__list">
|
<ul class="md-nav__list">
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#61-cachebackend-abstract" class="md-nav__link">
|
<a href="#81-cachebackend-abstract" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
6.1 CacheBackend (abstract)
|
8.1 CacheBackend (abstract)
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#62-cacheconfig" class="md-nav__link">
|
<a href="#82-cachestats" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
6.2 CacheConfig
|
8.2 CacheStats
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#63-inmemorycachebackend" class="md-nav__link">
|
<a href="#83-cacheconfig" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
6.3 InMemoryCacheBackend
|
8.3 CacheConfig
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#64-rediscachebackend" class="md-nav__link">
|
<a href="#84-inmemorycachebackend" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
6.4 RedisCacheBackend
|
8.4 InMemoryCacheBackend
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#65-cachedbaserepositoryt" class="md-nav__link">
|
<a href="#85-rediscachebackend" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
6.5 CachedBaseRepository[T]
|
8.5 RedisCacheBackend
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#86-cachedbaserepositoryt" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
8.6 CachedBaseRepository[T]
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -612,38 +633,38 @@
|
|||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#7-population-engine" class="md-nav__link">
|
<a href="#9-population-layer" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
7. Population Engine
|
9. Population Layer
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<nav class="md-nav" aria-label="7. Population Engine">
|
<nav class="md-nav" aria-label="9. Population Layer">
|
||||||
<ul class="md-nav__list">
|
<ul class="md-nav__list">
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#71-populaterule" class="md-nav__link">
|
<a href="#91-populaterule" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
7.1 PopulateRule
|
9.1 PopulateRule
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#72-populationengine" class="md-nav__link">
|
<a href="#92-populationengine" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
7.2 PopulationEngine
|
9.2 PopulationEngine
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#73-populatingrepositoryt" class="md-nav__link">
|
<a href="#93-circularreferenceerrorvalueerror" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
7.3 PopulatingRepository[T]
|
9.3 CircularReferenceError(ValueError)
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -654,8 +675,12 @@
|
|||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
<li class="md-nav__item">
|
||||||
</nav>
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
@@ -1157,100 +1182,252 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1>02 components</h1>
|
<h1 id="core-components">Core Components</h1>
|
||||||
|
<p>A validated reference to the public API. Signatures match the code in <code>mongo_ops/</code> exactly. See the use cases for runnable recipes.</p>
|
||||||
<h2 id="core-components">Core Components</h2>
|
<hr />
|
||||||
<h3 id="1-mongoconnectionmanager">1. MongoConnectionManager</h3>
|
<h2 id="1-mongoconnectionmanager">1. MongoConnectionManager</h2>
|
||||||
<p>Manages MongoDB connections with async lifecycle.
|
<p>Singleton-style owner of the Motor client and active database.</p>
|
||||||
<strong>Methods:</strong></p>
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Method</th>
|
||||||
|
<th>Signature</th>
|
||||||
|
<th>Behavior</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>connect</code></td>
|
||||||
|
<td><code>async (uri: str, db_name: str, **kwargs) -> AsyncIOMotorDatabase</code></td>
|
||||||
|
<td>Creates <code>AsyncIOMotorClient(uri, **kwargs)</code> and selects the database. Idempotent.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>disconnect</code></td>
|
||||||
|
<td><code>async () -> None</code></td>
|
||||||
|
<td>Closes the client and clears state.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>get_database</code></td>
|
||||||
|
<td><code>() -> AsyncIOMotorDatabase</code></td>
|
||||||
|
<td>Returns the active database; raises <code>RuntimeError("Database not connected. Call connect() first.")</code>.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>get_client</code></td>
|
||||||
|
<td><code>() -> AsyncIOMotorClient</code></td>
|
||||||
|
<td>Returns the active client; raises <code>RuntimeError("Client not connected. Call connect() first.")</code>.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>lifespan</code></td>
|
||||||
|
<td><code>async ctx manager (uri, db_name, **kwargs)</code></td>
|
||||||
|
<td><code>connect()</code> on entry, yields the database, <code>disconnect()</code> on exit. Designed for FastAPI lifespan.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<hr />
|
||||||
|
<h2 id="2-basedocument-pyobjectid">2. BaseDocument & PyObjectId</h2>
|
||||||
|
<p><code>BaseDocument(BaseModel)</code> — inherit for every Mongo entity.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>connect(uri, db_name, **kwargs)</code> - Connect to MongoDB</li>
|
<li><code>id: Optional[PyObjectId]</code> — aliased to <code>_id</code>, serialized to <code>str</code>.</li>
|
||||||
<li><code>disconnect()</code> - Close connection</li>
|
<li><code>created_at: datetime</code> — defaults to <code>datetime.utcnow()</code>.</li>
|
||||||
<li><code>get_database()</code> - Get current database instance</li>
|
<li><code>updated_at: datetime</code> — defaults to <code>datetime.utcnow()</code>.</li>
|
||||||
<li><code>get_client()</code> - Get current client instance</li>
|
<li><code>Config.populate_by_name = True</code>, <code>arbitrary_types_allowed = True</code>, <code>json_encoders = {ObjectId: str}</code>.</li>
|
||||||
<li><code>lifespan(uri, db_name, **kwargs)</code> - Context manager for FastAPI lifespan</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<h3 id="2-basedocument">2. BaseDocument</h3>
|
<p><code>PyObjectId(ObjectId)</code> — Pydantic v2-compatible <code>ObjectId</code> that accepts <code>str</code> or <code>ObjectId</code> and validates with <code>ObjectId.is_valid</code>.</p>
|
||||||
<p>Base model for all MongoDB documents. Provides:</p>
|
<hr />
|
||||||
|
<h2 id="3-crudmixint">3. CRUDMixin[T]</h2>
|
||||||
|
<p>Generic CRUD over a Motor collection. The building block of all repositories.</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Method</th>
|
||||||
|
<th>Signature</th>
|
||||||
|
<th>Notes</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>create</code></td>
|
||||||
|
<td><code>async (data: T) -> T</code></td>
|
||||||
|
<td>Dumps model (excludes <code>id</code>, <code>None</code>), stamps <code>created_at</code>/<code>updated_at</code>, inserts, returns model with assigned <code>_id</code>.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>get_by_id</code></td>
|
||||||
|
<td><code>async (id: str \| ObjectId) -> Optional[T]</code></td>
|
||||||
|
<td><code>str</code> is accepted and converted to <code>ObjectId</code>.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>get_many</code></td>
|
||||||
|
<td><code>async (filter: dict \| None = None, skip: int = 0, limit: int = 100, sort: list[tuple] \| None = None) -> list[T]</code></td>
|
||||||
|
<td>Cursor <code>.skip().limit().sort(...)</code> then <code>to_list(limit)</code>. <code>limit=0</code> disables the limit clause.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>update</code></td>
|
||||||
|
<td><code>async (id, data: dict[str, Any]) -> Optional[T]</code></td>
|
||||||
|
<td><code>$set</code> + refreshed <code>updated_at</code> via <code>find_one_and_update</code>.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>patch</code></td>
|
||||||
|
<td><code>async (id, data: dict[str, Any]) -> Optional[T]</code></td>
|
||||||
|
<td>Same as <code>update</code> but intended for REST PATCH semantics.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>delete</code></td>
|
||||||
|
<td><code>async (id) -> bool</code></td>
|
||||||
|
<td>True if a document was deleted.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>count</code></td>
|
||||||
|
<td><code>async (filter: dict \| None = None) -> int</code></td>
|
||||||
|
<td><code>count_documents</code>.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<hr />
|
||||||
|
<h2 id="4-baserepositoryt">4. BaseRepository[T]</h2>
|
||||||
|
<p><code>BaseRepository(collection_name: str, model: type[T])</code> — resolves the collection from <code>MongoConnectionManager.get_database()[collection_name]</code>. <strong>Requires an active connection at construction time.</strong> Provides everything in <code>CRUDMixin</code> plus <code>collection_name</code>.</p>
|
||||||
|
<hr />
|
||||||
|
<h2 id="5-populatingrepositoryt">5. PopulatingRepository[T]</h2>
|
||||||
|
<p><code>PopulatingRepository(collection_name, model, population_engine: PopulationEngine | None = None, populate_rules: list[PopulateRule] | None = None)</code>.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>id</code> (aliased to <code>_id</code>) - ObjectId</li>
|
<li><code>set_population_engine(engine)</code> / <code>set_populate_rules(rules)</code> — swap engine/rules at runtime.</li>
|
||||||
<li><code>created_at</code> - Auto-generated timestamp</li>
|
<li><code>data_to_model</code> hooks <code>_populate</code> — <code>get_by_id</code>/<code>get_many</code> return fully populated models.</li>
|
||||||
<li><code>updated_at</code> - Auto-updated timestamp</li>
|
<li><code>_depopulate(document)</code> — collapses populated FK fields back to <code>ObjectId</code> before <code>create</code>/<code>update</code>.</li>
|
||||||
|
<li><code>create</code> / <code>update</code> accept a <strong>model</strong> <code>T</code> (not a dict) so depopulation can run.</li>
|
||||||
|
<li><code>patch</code> blocks FK fields — raises <code>ValueError("Cannot patch FK fields via patch(): ... Use update() to change FK fields.")</code>.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3 id="3-baserepositoryt">3. BaseRepository[T]</h3>
|
<blockquote>
|
||||||
<p>Generic repository with CRUD operations:</p>
|
<p><strong>Populate semantics (important):</strong> a <code>PopulateRule</code> names a field that holds either an <code>ObjectId</code> or a <code>list[ObjectId]</code> <strong>and is the same field that gets replaced</strong> with the resolved document(s). There is no separate "ref field" vs "target field". See <a href="../03_use_cases/08_population/">use case 08</a>.</p>
|
||||||
|
</blockquote>
|
||||||
|
<hr />
|
||||||
|
<h2 id="6-transactionmanager">6. TransactionManager</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Method</th>
|
||||||
|
<th>Signature</th>
|
||||||
|
<th>Behavior</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>start_session</code></td>
|
||||||
|
<td><code>async ctx manager (**kwargs) -> AsyncIOMotorClientSession</code></td>
|
||||||
|
<td>Yields a session with an active transaction. Pass <code>session=</code> to every collection call inside.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>execute_transaction</code></td>
|
||||||
|
<td><code>async (operations: list[Callable[[session], Awaitable[Any]]], **kwargs) -> list[Any]</code></td>
|
||||||
|
<td>Runs each op inside one transaction and returns results in order; any exception aborts the transaction and propagates.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<hr />
|
||||||
|
<h2 id="7-modelregistry">7. ModelRegistry</h2>
|
||||||
|
<p>Centralized models, indexes, and cache lifecycle for multi-collection services.</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Method</th>
|
||||||
|
<th>Signature</th>
|
||||||
|
<th>Behavior</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>register</code></td>
|
||||||
|
<td><code>(collection_name: str, model: type[BaseDocument], indexes: list[Any] \| None = None)</code></td>
|
||||||
|
<td>Records model + index specs. Index specs are passed <strong>as-is</strong> to pymongo <code>create_index</code> — single tuples, compound lists, or dicts with <code>keys</code>/<code>options</code>.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>initialize_all</code></td>
|
||||||
|
<td><code>async (db: AsyncIOMotorDatabase \| None = None) -> None</code></td>
|
||||||
|
<td><code>create_index</code> per registered spec (idempotent). Uses the manager database if <code>db</code> omitted.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>get_model</code></td>
|
||||||
|
<td><code>(collection_name) -> type[BaseDocument]</code></td>
|
||||||
|
<td>Raises <code>KeyError</code> if unregistered.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>list_collections</code></td>
|
||||||
|
<td><code>() -> list[str]</code></td>
|
||||||
|
<td>Registered collection names.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>set_cache_backend</code></td>
|
||||||
|
<td><code>(backend: CacheBackend) -> None</code></td>
|
||||||
|
<td>Register the single shared backend.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>initialize_cache</code></td>
|
||||||
|
<td><code>async () -> None</code></td>
|
||||||
|
<td>Starts the backend (background TTL cleanup) — raises <code>RuntimeError</code> if no backend registered.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>shutdown_cache</code></td>
|
||||||
|
<td><code>async () -> None</code></td>
|
||||||
|
<td>Stops the backend cleanly and clears it.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>get_cache_backend</code></td>
|
||||||
|
<td><code>() -> Optional[CacheBackend]</code></td>
|
||||||
|
<td>Current backend, if any.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<hr />
|
||||||
|
<h2 id="8-cache-layer">8. Cache Layer</h2>
|
||||||
|
<h3 id="81-cachebackend-abstract">8.1 CacheBackend (abstract)</h3>
|
||||||
|
<p>Async interface: <code>get(key) -> Optional[bytes]</code>, <code>set(key, value: bytes, ttl | None)</code>, <code>delete(key)</code>, <code>exists(key)</code>, <code>clear_pattern(pattern)</code>, <code>get_stats() -> CacheStats</code>, <code>initialize()</code>, <code>shutdown()</code>. Values are <strong>bytes</strong> (JSON-encoded).</p>
|
||||||
|
<h3 id="82-cachestats">8.2 CacheStats</h3>
|
||||||
|
<p>Dataclass: <code>hits</code>, <code>misses</code>, <code>sets</code>, <code>deletes</code>, <code>current_size</code>, <code>max_size</code>.</p>
|
||||||
|
<h3 id="83-cacheconfig">8.3 CacheConfig</h3>
|
||||||
|
<p>Dataclass: <code>enabled: bool = True</code>, <code>backend: Literal["memory", "redis"] = "memory"</code>, <code>redis_client</code>, <code>default_ttl: int = 300</code>, <code>max_entries: int = 10000</code>, <code>key_prefix: str = ""</code>, <code>cleanup_interval: int = 60</code>. Raises <code>ValueError</code> if <code>backend="redis"</code> without a client, and <code>ImportError</code> if <code>redis</code> is not installed.</p>
|
||||||
|
<h3 id="84-inmemorycachebackend">8.4 InMemoryCacheBackend</h3>
|
||||||
|
<p><code>InMemoryCacheBackend(max_entries=10000, default_ttl=300, cleanup_interval=60)</code> — LRU <code>OrderedDict</code> + TTL heap; <code>initialize()</code> spawns the periodic cleanup task (TTL 0 expires immediately).</p>
|
||||||
|
<h3 id="85-rediscachebackend">8.5 RedisCacheBackend</h3>
|
||||||
|
<p><code>RedisCacheBackend(redis_client, key_prefix="", default_ttl=300)</code> — <code>setex</code> storage, SCAN-based <code>clear_pattern</code>, and <code>publish_invalidate(key)</code> for cross-service invalidation on <code>delete</code> via the <code>mongo_ops:cache:invalidate</code> channel.</p>
|
||||||
|
<h3 id="86-cachedbaserepositoryt">8.6 CachedBaseRepository[T]</h3>
|
||||||
|
<p><code>CachedBaseRepository(collection_name, model, cache_backend: CacheBackend, config: CacheConfig | None = None)</code>.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>create(data: T) -> T</code></li>
|
<li>Cache keys are <code>"{key_prefix}{id}"</code> (prefix defaults to <code>"{collection_name}:"</code>).</li>
|
||||||
<li><code>get_by_id(id: str | ObjectId) -> Optional[T]</code></li>
|
<li><code>get_by_id</code> — cache-first; cache miss reads DB and stores <code>model_dump(by_alias=True)</code> (JSON-encoded) for <code>default_ttl</code>. Honors <code>config.enabled=False</code> (bypass).</li>
|
||||||
<li><code>get_many(filter, skip, limit, sort) -> List[T]</code></li>
|
<li><code>create</code> — inserts then caches the result.</li>
|
||||||
<li><code>update(id, data: Dict) -> Optional[T]</code></li>
|
<li><code>update</code>/<code>delete</code> — refresh or remove the cache entry.</li>
|
||||||
<li><code>delete(id) -> bool</code></li>
|
<li><code>warm_cache(ids) -> int</code> — prefetch a list of IDs, returns count warmed.</li>
|
||||||
<li><code>count(filter) -> int</code></li>
|
<li><code>invalidate_cache(id)</code> — manual eviction.</li>
|
||||||
</ul>
|
|
||||||
<h3 id="4-transactionmanager">4. TransactionManager</h3>
|
|
||||||
<p>Handles multi-document transactions:</p>
|
|
||||||
<ul>
|
|
||||||
<li><code>start_session()</code> - Context manager for transactions</li>
|
|
||||||
<li><code>execute_transaction(operations)</code> - Execute multiple operations atomically</li>
|
|
||||||
</ul>
|
|
||||||
<h3 id="5-modelregistry">5. ModelRegistry</h3>
|
|
||||||
<p>Register and initialize models:</p>
|
|
||||||
<ul>
|
|
||||||
<li><code>register(collection_name, model, indexes)</code> - Register a model</li>
|
|
||||||
<li><code>initialize_all()</code> - Create all indexes</li>
|
|
||||||
<li><code>get_model(collection_name)</code> - Get registered model</li>
|
|
||||||
<li><code>list_collections()</code> - List all registered collections</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
<h3 id="6-cache-backend">6. Cache Backend</h3>
|
<h2 id="9-population-layer">9. Population Layer</h2>
|
||||||
<h4 id="61-cachebackend-abstract">6.1 CacheBackend (abstract)</h4>
|
<h3 id="91-populaterule">9.1 PopulateRule</h3>
|
||||||
|
<p>Dataclass:</p>
|
||||||
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="nd">@dataclass</span>
|
||||||
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="k">class</span><span class="w"> </span><span class="nc">PopulateRule</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a> <span class="n">field_name</span><span class="p">:</span> <span class="nb">str</span> <span class="c1"># field holding the ObjectId / list[ObjectId]; replaced in-place with the resolved doc(s)</span>
|
||||||
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a> <span class="n">collection_name</span><span class="p">:</span> <span class="nb">str</span> <span class="c1"># collection the references point at</span>
|
||||||
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a> <span class="n">nested_rules</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">PopulateRule</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||||||
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a> <span class="n">max_depth</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1</span>
|
||||||
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a> <span class="nb">filter</span><span class="p">:</span> <span class="nb">dict</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># DECLARED but NOT yet applied by the engine</span>
|
||||||
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="n">projection</span><span class="p">:</span> <span class="nb">dict</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># DECLARED but NOT yet applied by the engine</span>
|
||||||
|
</code></pre></div>
|
||||||
|
<blockquote>
|
||||||
|
<p>⚠️ <code>filter</code> and <code>projection</code> are accepted but currently <strong>ignored</strong> by <code>PopulationEngine</code> — do not rely on them yet.</p>
|
||||||
|
</blockquote>
|
||||||
|
<h3 id="92-populationengine">9.2 PopulationEngine</h3>
|
||||||
|
<p><code>PopulationEngine(repos: dict[str, Any], global_max_depth: int = 10)</code> — <code>repos</code> maps <code>collection_name</code> → repository.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Defines the async interface for cache operations: <code>get</code>, <code>set</code>, <code>delete</code>, <code>exists</code>, <code>clear_pattern</code>, <code>get_stats</code>, <code>initialize</code>, <code>shutdown</code>.</li>
|
<li><code>register_repo(collection_name, repo)</code> — add repositories at runtime.</li>
|
||||||
<li>Provides <code>CacheStats</code> for hit/miss/size metrics and <code>CircularReferenceError</code> for cycle detection.</li>
|
<li><code>populate(document, rules, depth=0)</code> — resolves refs recursively, replacing <code>field_name</code> in place; raises <code>CircularReferenceError(collection, doc_id, path)</code> when a <code>(Class, id)</code> pair is revisited.</li>
|
||||||
</ul>
|
<li><code>depopulate(document, rules)</code> — collapse populated docs back to IDs (for storage).</li>
|
||||||
<h4 id="62-cacheconfig">6.2 CacheConfig</h4>
|
<li><code>global_max_depth</code> caps recursion; per-rule <code>max_depth</code> bounds a rule's descent.</li>
|
||||||
<ul>
|
|
||||||
<li>Dataclass to configure caching (<code>enabled</code>, <code>backend</code>, <code>redis_client</code>, <code>default_ttl</code>, <code>max_entries</code>, <code>key_prefix</code>, <code>cleanup_interval</code>).</li>
|
|
||||||
</ul>
|
|
||||||
<h4 id="63-inmemorycachebackend">6.3 InMemoryCacheBackend</h4>
|
|
||||||
<ul>
|
|
||||||
<li>In‑process cache using an LRU <code>OrderedDict</code> and a TTL heap.</li>
|
|
||||||
<li>Background task periodically evicts expired entries.</li>
|
|
||||||
</ul>
|
|
||||||
<h4 id="64-rediscachebackend">6.4 RedisCacheBackend</h4>
|
|
||||||
<ul>
|
|
||||||
<li>Distributed cache based on <code>redis.asyncio</code>.</li>
|
|
||||||
<li>JSON serialisation, optional pub/sub invalidation.</li>
|
|
||||||
</ul>
|
|
||||||
<h4 id="65-cachedbaserepositoryt">6.5 CachedBaseRepository[T]</h4>
|
|
||||||
<ul>
|
|
||||||
<li>Extends <code>BaseRepository</code> with transparent ID‑based caching.</li>
|
|
||||||
<li>Methods:</li>
|
|
||||||
<li><code>get_by_id</code> – cache‑first lookup.</li>
|
|
||||||
<li><code>create</code> – stores newly created doc in cache.</li>
|
|
||||||
<li><code>update</code> / <code>delete</code> – invalidate or refresh cache.</li>
|
|
||||||
<li><code>warm_cache(ids)</code> – pre‑load a list of IDs.</li>
|
|
||||||
<li><code>invalidate_cache(id)</code> – manual invalidation.</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
<h3 id="93-circularreferenceerrorvalueerror">9.3 CircularReferenceError(ValueError)</h3>
|
||||||
|
<p>Holds <code>collection</code>, <code>doc_id</code>, and the visited <code>path</code> for debugging cycle messages.</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h3 id="7-population-engine">7. Population Engine</h3>
|
<h2 id="related">Related</h2>
|
||||||
<h4 id="71-populaterule">7.1 PopulateRule</h4>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Dataclass defining a population rule: <code>field_name</code>, <code>collection_name</code>, <code>ref_field</code>, optional <code>nested_rules</code>, <code>max_depth</code>, <code>filter</code>, <code>projection</code>.</li>
|
<li><a href="../03_use_cases/01_basic_crud/">Use case 01 – Basic CRUD</a> · <a href="../03_use_cases/07_caching/">07 – Caching</a> · <a href="../03_use_cases/08_population/">08 – Population</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<h4 id="72-populationengine">7.2 PopulationEngine</h4>
|
|
||||||
<ul>
|
|
||||||
<li>Recursively resolves references according to <code>PopulateRule</code> list.</li>
|
|
||||||
<li>Detects circular references and raises <code>CircularReferenceError</code>.</li>
|
|
||||||
<li>Supports per‑rule depth limits, filters and projections.</li>
|
|
||||||
</ul>
|
|
||||||
<h4 id="73-populatingrepositoryt">7.3 PopulatingRepository[T]</h4>
|
|
||||||
<ul>
|
|
||||||
<li>Extends <code>BaseRepository</code> to automatically populate related documents.</li>
|
|
||||||
<li>Accepts an optional <code>PopulationEngine</code> and a list of <code>PopulateRule</code>.</li>
|
|
||||||
<li>Provides <code>_populate</code> and <code>_depopulate</code> helpers used in <code>get_by_id</code> and elsewhere.</li>
|
|
||||||
</ul>
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -530,6 +530,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||||
|
|
||||||
|
|
||||||
@@ -562,6 +564,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__title" for="__toc">
|
<label class="md-nav__title" for="__toc">
|
||||||
<span class="md-nav__icon md-icon"></span>
|
<span class="md-nav__icon md-icon"></span>
|
||||||
Table of contents
|
Table of contents
|
||||||
@@ -569,9 +573,36 @@
|
|||||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#use-case-1-basic-fastapi-crud-api" class="md-nav__link">
|
<a href="#whats-new" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Use Case 1: Basic FastAPI CRUD API
|
📦 What's New?
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#example" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
🚀 Example
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#tips" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
💡 Tips
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -1004,114 +1035,144 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1>Basic CRUD</h1>
|
<h1 id="use-case-1-basic-fastapi-crud-api">Use Case 1: Basic FastAPI CRUD API</h1>
|
||||||
|
<p><strong>Scenario:</strong> A simple user management API with CRUD endpoints, index registration, and a correct connection lifecycle.</p>
|
||||||
<h3 id="use-case-1-basic-fastapi-crud-api">Use Case 1: Basic FastAPI CRUD API</h3>
|
<hr />
|
||||||
<p><strong>Scenario:</strong> Create a simple user management API with CRUD operations.</p>
|
<h2 id="whats-new">📦 What's New?</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Component</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>MongoConnectionManager.lifespan</code></td>
|
||||||
|
<td>Connect on startup, disconnect on shutdown.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>BaseRepository[User]</code></td>
|
||||||
|
<td>Generic CRUD — no endpoint-level Mongo code.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>ModelRegistry.register</code></td>
|
||||||
|
<td>Declares the <code>email</code> index; <code>initialize_all</code> creates it at startup.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<hr />
|
||||||
|
<h2 id="example">🚀 Example</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span><span class="p">,</span> <span class="n">Depends</span><span class="p">,</span> <span class="n">HTTPException</span>
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">pydantic</span><span class="w"> </span><span class="kn">import</span> <span class="n">Field</span>
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">Depends</span><span class="p">,</span> <span class="n">FastAPI</span><span class="p">,</span> <span class="n">HTTPException</span>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="p">(</span>
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">pydantic</span><span class="w"> </span><span class="kn">import</span> <span class="n">Field</span>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a> <span class="n">MongoConnectionManager</span><span class="p">,</span>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a> <span class="n">BaseDocument</span><span class="p">,</span>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">BaseRepository</span><span class="p">,</span> <span class="n">ModelRegistry</span><span class="p">,</span> <span class="n">MongoConnectionManager</span>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="n">BaseRepository</span><span class="p">,</span>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> <span class="n">ModelRegistry</span><span class="p">,</span>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a><span class="p">)</span>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a><span class="c1"># Model</span>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a>
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a><span class="c1"># ---------------------------</span>
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a><span class="c1"># Model Definition</span>
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="o">...</span><span class="p">,</span> <span class="n">min_length</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a><span class="c1"># ---------------------------</span>
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="n">is_active</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="o">...</span><span class="p">,</span> <span class="n">min_length</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="n">is_active</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span>
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a>
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a><span class="c1"># Repository</span>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a>
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="c1"># ---------------------------</span>
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="k">class</span><span class="w"> </span><span class="nc">UserRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">User</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="c1"># Repository</span>
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a><span class="c1"># ---------------------------</span>
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">User</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a><span class="k">class</span><span class="w"> </span><span class="nc">UserRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">User</span><span class="p">]):</span>
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a>
|
||||||
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a>
|
||||||
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">User</span><span class="p">)</span>
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a>
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a><span class="c1"># Registration + index</span>
|
||||||
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a>
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a><span class="c1"># Register the model only once</span>
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">User</span><span class="p">,</span> <span class="n">indexes</span><span class="o">=</span><span class="p">[(</span><span class="s2">"email"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)])</span>
|
||||||
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">User</span><span class="p">,</span> <span class="n">indexes</span><span class="o">=</span><span class="p">[(</span><span class="s2">"email"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)])</span>
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a>
|
||||||
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a>
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a>
|
||||||
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a>
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a><span class="c1"># ---------------------------</span>
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a><span class="c1"># Lifecycle</span>
|
||||||
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a><span class="c1"># FastAPI Lifespan</span>
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a><span class="c1"># ---------------------------</span>
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a><span class="nd">@asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a><span class="nd">@asynccontextmanager</span>
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">_app</span><span class="p">:</span> <span class="n">FastAPI</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">_app</span><span class="p">:</span> <span class="n">FastAPI</span><span class="p">):</span>
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a><span class="w"> </span><span class="sd">"""Manage MongoDB connection during the app lifecycle."""</span>
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="n">uri</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"MONGO_URI"</span><span class="p">,</span> <span class="s2">"mongodb://localhost:27017"</span><span class="p">),</span>
|
||||||
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> <span class="n">db_name</span><span class="o">=</span><span class="s2">"mydb"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span>
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="p">):</span>
|
||||||
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="n">db_name</span><span class="o">=</span><span class="s2">"mydb"</span>
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="p">):</span>
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="k">yield</span>
|
||||||
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a>
|
||||||
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="k">yield</span>
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a>
|
||||||
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a>
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">(</span><span class="n">lifespan</span><span class="o">=</span><span class="n">lifespan</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a>
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a>
|
||||||
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">(</span><span class="n">lifespan</span><span class="o">=</span><span class="n">lifespan</span><span class="p">)</span>
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a>
|
||||||
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a>
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a>
|
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a><span class="c1"># Dependency — created per request, after connect()</span>
|
||||||
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a><span class="c1"># ---------------------------</span>
|
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a><span class="c1"># Dependency Injection</span>
|
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a><span class="k">def</span><span class="w"> </span><span class="nf">get_user_repository</span><span class="p">()</span> <span class="o">-></span> <span class="n">UserRepository</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a><span class="c1"># ---------------------------</span>
|
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a> <span class="k">return</span> <span class="n">UserRepository</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a><span class="k">def</span><span class="w"> </span><span class="nf">get_user_repository</span><span class="p">()</span> <span class="o">-></span> <span class="n">UserRepository</span><span class="p">:</span>
|
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a>
|
||||||
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a><span class="w"> </span><span class="sd">"""Dependency-injected repository for users."""</span>
|
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a>
|
||||||
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="k">return</span> <span class="n">UserRepository</span><span class="p">()</span>
|
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a>
|
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a><span class="c1"># Routes</span>
|
||||||
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a>
|
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a><span class="c1"># ---------------------------</span>
|
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/users/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">User</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a><span class="c1"># Routes</span>
|
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_user</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span><span class="p">,</span> <span class="n">repo</span><span class="p">:</span> <span class="n">UserRepository</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_user_repository</span><span class="p">)):</span>
|
||||||
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a><span class="c1"># ---------------------------</span>
|
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/users/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">User</span><span class="p">)</span>
|
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a>
|
||||||
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_user</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span><span class="p">,</span> <span class="n">repo</span><span class="p">:</span> <span class="n">UserRepository</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_user_repository</span><span class="p">)):</span>
|
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a>
|
||||||
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/users/</span><span class="si">{user_id}</span><span class="s2">"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">User</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a>
|
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_user</span><span class="p">(</span><span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">repo</span><span class="p">:</span> <span class="n">UserRepository</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_user_repository</span><span class="p">)):</span>
|
||||||
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a>
|
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a> <span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/users/</span><span class="si">{user_id}</span><span class="s2">"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">User</span><span class="p">)</span>
|
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_user</span><span class="p">(</span><span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">repo</span><span class="p">:</span> <span class="n">UserRepository</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_user_repository</span><span class="p">)):</span>
|
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"User not found"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a> <span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
|
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a> <span class="k">return</span> <span class="n">user</span>
|
||||||
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="p">:</span>
|
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a>
|
||||||
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"User not found"</span><span class="p">)</span>
|
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a>
|
||||||
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a> <span class="k">return</span> <span class="n">user</span>
|
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/users/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="nb">list</span><span class="p">[</span><span class="n">User</span><span class="p">])</span>
|
||||||
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a>
|
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_users</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a>
|
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a> <span class="n">skip</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/users/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="nb">list</span><span class="p">[</span><span class="n">User</span><span class="p">])</span>
|
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a> <span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_users</span><span class="p">(</span>
|
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a> <span class="n">repo</span><span class="p">:</span> <span class="n">UserRepository</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_user_repository</span><span class="p">),</span>
|
||||||
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a> <span class="n">skip</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
|
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a><span class="p">):</span>
|
||||||
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a> <span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
|
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span><span class="n">skip</span><span class="o">=</span><span class="n">skip</span><span class="p">,</span> <span class="n">limit</span><span class="o">=</span><span class="n">limit</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a> <span class="n">repo</span><span class="p">:</span> <span class="n">UserRepository</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_user_repository</span><span class="p">),</span>
|
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a>
|
||||||
<a id="__codelineno-0-79" name="__codelineno-0-79" href="#__codelineno-0-79"></a><span class="p">):</span>
|
<a id="__codelineno-0-79" name="__codelineno-0-79" href="#__codelineno-0-79"></a>
|
||||||
<a id="__codelineno-0-80" name="__codelineno-0-80" href="#__codelineno-0-80"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span><span class="n">skip</span><span class="o">=</span><span class="n">skip</span><span class="p">,</span> <span class="n">limit</span><span class="o">=</span><span class="n">limit</span><span class="p">)</span>
|
<a id="__codelineno-0-80" name="__codelineno-0-80" href="#__codelineno-0-80"></a><span class="nd">@app</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="s2">"/users/</span><span class="si">{user_id}</span><span class="s2">"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">User</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-81" name="__codelineno-0-81" href="#__codelineno-0-81"></a>
|
<a id="__codelineno-0-81" name="__codelineno-0-81" href="#__codelineno-0-81"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_user</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-82" name="__codelineno-0-82" href="#__codelineno-0-82"></a>
|
<a id="__codelineno-0-82" name="__codelineno-0-82" href="#__codelineno-0-82"></a> <span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-83" name="__codelineno-0-83" href="#__codelineno-0-83"></a><span class="nd">@app</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="s2">"/users/</span><span class="si">{user_id}</span><span class="s2">"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">User</span><span class="p">)</span>
|
<a id="__codelineno-0-83" name="__codelineno-0-83" href="#__codelineno-0-83"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-84" name="__codelineno-0-84" href="#__codelineno-0-84"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_user</span><span class="p">(</span>
|
<a id="__codelineno-0-84" name="__codelineno-0-84" href="#__codelineno-0-84"></a> <span class="n">repo</span><span class="p">:</span> <span class="n">UserRepository</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_user_repository</span><span class="p">),</span>
|
||||||
<a id="__codelineno-0-85" name="__codelineno-0-85" href="#__codelineno-0-85"></a> <span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
<a id="__codelineno-0-85" name="__codelineno-0-85" href="#__codelineno-0-85"></a><span class="p">):</span>
|
||||||
<a id="__codelineno-0-86" name="__codelineno-0-86" href="#__codelineno-0-86"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
<a id="__codelineno-0-86" name="__codelineno-0-86" href="#__codelineno-0-86"></a> <span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">user_id</span><span class="p">,</span> <span class="p">{</span><span class="s2">"email"</span><span class="p">:</span> <span class="n">email</span><span class="p">})</span>
|
||||||
<a id="__codelineno-0-87" name="__codelineno-0-87" href="#__codelineno-0-87"></a> <span class="n">repo</span><span class="p">:</span> <span class="n">UserRepository</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_user_repository</span><span class="p">),</span>
|
<a id="__codelineno-0-87" name="__codelineno-0-87" href="#__codelineno-0-87"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-88" name="__codelineno-0-88" href="#__codelineno-0-88"></a><span class="p">):</span>
|
<a id="__codelineno-0-88" name="__codelineno-0-88" href="#__codelineno-0-88"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"User not found"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-89" name="__codelineno-0-89" href="#__codelineno-0-89"></a> <span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">user_id</span><span class="p">,</span> <span class="p">{</span><span class="s2">"email"</span><span class="p">:</span> <span class="n">email</span><span class="p">})</span>
|
<a id="__codelineno-0-89" name="__codelineno-0-89" href="#__codelineno-0-89"></a> <span class="k">return</span> <span class="n">user</span>
|
||||||
<a id="__codelineno-0-90" name="__codelineno-0-90" href="#__codelineno-0-90"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="p">:</span>
|
<a id="__codelineno-0-90" name="__codelineno-0-90" href="#__codelineno-0-90"></a>
|
||||||
<a id="__codelineno-0-91" name="__codelineno-0-91" href="#__codelineno-0-91"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"User not found"</span><span class="p">)</span>
|
<a id="__codelineno-0-91" name="__codelineno-0-91" href="#__codelineno-0-91"></a>
|
||||||
<a id="__codelineno-0-92" name="__codelineno-0-92" href="#__codelineno-0-92"></a> <span class="k">return</span> <span class="n">user</span>
|
<a id="__codelineno-0-92" name="__codelineno-0-92" href="#__codelineno-0-92"></a><span class="nd">@app</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s2">"/users/</span><span class="si">{user_id}</span><span class="s2">"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-93" name="__codelineno-0-93" href="#__codelineno-0-93"></a>
|
<a id="__codelineno-0-93" name="__codelineno-0-93" href="#__codelineno-0-93"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">delete_user</span><span class="p">(</span><span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">repo</span><span class="p">:</span> <span class="n">UserRepository</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_user_repository</span><span class="p">)):</span>
|
||||||
<a id="__codelineno-0-94" name="__codelineno-0-94" href="#__codelineno-0-94"></a>
|
<a id="__codelineno-0-94" name="__codelineno-0-94" href="#__codelineno-0-94"></a> <span class="n">deleted</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-95" name="__codelineno-0-95" href="#__codelineno-0-95"></a><span class="nd">@app</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s2">"/users/</span><span class="si">{user_id}</span><span class="s2">"</span><span class="p">)</span>
|
<a id="__codelineno-0-95" name="__codelineno-0-95" href="#__codelineno-0-95"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">deleted</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-96" name="__codelineno-0-96" href="#__codelineno-0-96"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">delete_user</span><span class="p">(</span>
|
<a id="__codelineno-0-96" name="__codelineno-0-96" href="#__codelineno-0-96"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"User not found"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-97" name="__codelineno-0-97" href="#__codelineno-0-97"></a> <span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
<a id="__codelineno-0-97" name="__codelineno-0-97" href="#__codelineno-0-97"></a> <span class="k">return</span> <span class="p">{</span><span class="s2">"message"</span><span class="p">:</span> <span class="s2">"User deleted successfully"</span><span class="p">}</span>
|
||||||
<a id="__codelineno-0-98" name="__codelineno-0-98" href="#__codelineno-0-98"></a> <span class="n">repo</span><span class="p">:</span> <span class="n">UserRepository</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_user_repository</span><span class="p">),</span>
|
|
||||||
<a id="__codelineno-0-99" name="__codelineno-0-99" href="#__codelineno-0-99"></a><span class="p">):</span>
|
|
||||||
<a id="__codelineno-0-100" name="__codelineno-0-100" href="#__codelineno-0-100"></a> <span class="n">deleted</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
|
|
||||||
<a id="__codelineno-0-101" name="__codelineno-0-101" href="#__codelineno-0-101"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">deleted</span><span class="p">:</span>
|
|
||||||
<a id="__codelineno-0-102" name="__codelineno-0-102" href="#__codelineno-0-102"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"User not found"</span><span class="p">)</span>
|
|
||||||
<a id="__codelineno-0-103" name="__codelineno-0-103" href="#__codelineno-0-103"></a> <span class="k">return</span> <span class="p">{</span><span class="s2">"message"</span><span class="p">:</span> <span class="s2">"User deleted successfully"</span><span class="p">}</span>
|
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
|
<hr />
|
||||||
|
<h2 id="tips">💡 Tips</h2>
|
||||||
|
<ul>
|
||||||
|
<li><code>UserRepository()</code> is created <strong>inside the dependency</strong>, i.e., only after the lifespan has connected. Instantiating a repository at module import time raises <code>RuntimeError</code> because the database isn't connected yet.</li>
|
||||||
|
<li><code>BaseRepository.update(id, {...})</code> takes a <strong>dict</strong>; only <code>PopulatingRepository.update</code> takes a model.</li>
|
||||||
|
<li>Use <code>patch()</code> for REST PATCH semantics — it accepts a partial dict like <code>update</code>, but <code>PopulatingRepository</code> rejects FK fields.</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../02_custom_repo/">02 – Custom repository</a> · <a href="../04_pagination/">04 – Pagination</a> · <a href="../../01_overview/">Overview</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -553,6 +553,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||||
|
|
||||||
|
|
||||||
@@ -585,6 +587,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__title" for="__toc">
|
<label class="md-nav__title" for="__toc">
|
||||||
<span class="md-nav__icon md-icon"></span>
|
<span class="md-nav__icon md-icon"></span>
|
||||||
Table of contents
|
Table of contents
|
||||||
@@ -592,9 +596,36 @@
|
|||||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#use-case-2-custom-repository-with-business-logic" class="md-nav__link">
|
<a href="#whats-new" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Use Case 2: Custom Repository with Business Logic
|
📦 What's New?
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#example" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
🚀 Example
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#tips" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
💡 Tips
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -1004,81 +1035,127 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1>Custom Repo</h1>
|
<h1 id="use-case-2-custom-repository-with-business-logic">Use Case 2: Custom Repository with Business Logic</h1>
|
||||||
|
<p><strong>Scenario:</strong> An e-commerce product catalog needs search, filtering, and stock updates without Mongo leaking into routes.</p>
|
||||||
<h3 id="use-case-2-custom-repository-with-business-logic">Use Case 2: Custom Repository with Business Logic</h3>
|
<hr />
|
||||||
<p><strong>Scenario:</strong> E-commerce product catalog with custom search and filtering.</p>
|
<h2 id="whats-new">📦 What's New?</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">BaseRepository</span>
|
<table>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">List</span>
|
<thead>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">pydantic</span><span class="w"> </span><span class="kn">import</span> <span class="n">Field</span>
|
<tr>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a>
|
<th>Component</th>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="k">class</span><span class="w"> </span><span class="nc">Product</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<th>Description</th>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="o">...</span><span class="p">,</span> <span class="n">min_length</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
|
</tr>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a> <span class="n">description</span><span class="p">:</span> <span class="nb">str</span>
|
</thead>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="n">price</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="o">...</span><span class="p">,</span> <span class="n">gt</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
|
<tbody>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> <span class="n">category</span><span class="p">:</span> <span class="nb">str</span>
|
<tr>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="n">in_stock</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span>
|
<td>Repository methods</td>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> <span class="n">quantity</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">ge</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
|
<td>Encapsulate queries (<code>$regex</code>, filters, <code>$inc</code>) behind domain methods.</td>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> <span class="n">tags</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
</tr>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a>
|
<tr>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a><span class="k">class</span><span class="w"> </span><span class="nc">ProductRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Product</span><span class="p">]):</span>
|
<td><code>get_many</code></td>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<td>Filtering + default pagination via the base repository.</td>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"products"</span><span class="p">,</span> <span class="n">Product</span><span class="p">)</span>
|
</tr>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a>
|
<tr>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">search_by_name</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">query</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="n">Product</span><span class="p">]:</span>
|
<td>Direct <code>collection</code> access</td>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a><span class="w"> </span><span class="sd">"""Search products by name (case-insensitive)"""</span>
|
<td>For operations with no base-repo helper (regex search, atomic <code>$inc</code>).</td>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> <span class="n">docs</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">find</span><span class="p">({</span>
|
</tr>
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> <span class="s2">"name"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"$regex"</span><span class="p">:</span> <span class="n">query</span><span class="p">,</span> <span class="s2">"$options"</span><span class="p">:</span> <span class="s2">"i"</span><span class="p">}</span>
|
</tbody>
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="p">})</span><span class="o">.</span><span class="n">to_list</span><span class="p">(</span><span class="n">length</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
|
</table>
|
||||||
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="k">return</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="p">(</span><span class="o">**</span><span class="n">doc</span><span class="p">)</span> <span class="k">for</span> <span class="n">doc</span> <span class="ow">in</span> <span class="n">docs</span><span class="p">]</span>
|
<hr />
|
||||||
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a>
|
<h2 id="example">🚀 Example</h2>
|
||||||
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_by_category</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">category</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">in_stock_only</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="n">Product</span><span class="p">]:</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span><span class="p">,</span> <span class="n">HTTPException</span><span class="p">,</span> <span class="n">Query</span>
|
||||||
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a><span class="w"> </span><span class="sd">"""Get products by category"""</span>
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">BaseRepository</span>
|
||||||
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a> <span class="n">filter_query</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"category"</span><span class="p">:</span> <span class="n">category</span><span class="p">}</span>
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>
|
||||||
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a> <span class="k">if</span> <span class="n">in_stock_only</span><span class="p">:</span>
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a> <span class="n">filter_query</span><span class="p">[</span><span class="s2">"in_stock"</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span>
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="c1"># Model</span>
|
||||||
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span><span class="nb">filter</span><span class="o">=</span><span class="n">filter_query</span><span class="p">)</span>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="k">class</span><span class="w"> </span><span class="nc">Product</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_low_stock</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">threshold</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="n">Product</span><span class="p">]:</span>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
|
||||||
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a><span class="w"> </span><span class="sd">"""Get products with low stock"""</span>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> <span class="n">description</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="n">price</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="mf">0.0</span>
|
||||||
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="nb">filter</span><span class="o">=</span><span class="p">{</span><span class="s2">"quantity"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"$lt"</span><span class="p">:</span> <span class="n">threshold</span><span class="p">},</span> <span class="s2">"in_stock"</span><span class="p">:</span> <span class="kc">True</span><span class="p">}</span>
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> <span class="n">category</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="p">)</span>
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> <span class="n">in_stock</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span>
|
||||||
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a>
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">quantity</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
|
||||||
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_stock</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">product_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">quantity_delta</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="n">Product</span><span class="p">]:</span>
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">tags</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
||||||
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a><span class="w"> </span><span class="sd">"""Update product stock (increment/decrement)"""</span>
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a>
|
||||||
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">find_one_and_update</span><span class="p">(</span>
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a>
|
||||||
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="p">{</span><span class="s2">"_id"</span><span class="p">:</span> <span class="n">ObjectId</span><span class="p">(</span><span class="n">product_id</span><span class="p">)},</span>
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="p">{</span><span class="s2">"$inc"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"quantity"</span><span class="p">:</span> <span class="n">quantity_delta</span><span class="p">},</span> <span class="s2">"$set"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"updated_at"</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()}},</span>
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a><span class="c1"># Repository</span>
|
||||||
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="n">return_document</span><span class="o">=</span><span class="kc">True</span>
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="p">)</span>
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a><span class="k">class</span><span class="w"> </span><span class="nc">ProductRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Product</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="p">(</span><span class="o">**</span><span class="n">result</span><span class="p">)</span> <span class="k">if</span> <span class="n">result</span> <span class="k">else</span> <span class="kc">None</span>
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a>
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"products"</span><span class="p">,</span> <span class="n">Product</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a><span class="c1"># Usage in FastAPI</span>
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a>
|
||||||
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span><span class="p">,</span> <span class="n">Query</span>
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">search_by_name</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">query</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="n">Product</span><span class="p">]:</span>
|
||||||
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a>
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a><span class="w"> </span><span class="sd">"""Case-insensitive name search."""</span>
|
||||||
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">()</span>
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> <span class="n">docs</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">find</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a><span class="n">product_repo</span> <span class="o">=</span> <span class="n">ProductRepository</span><span class="p">()</span>
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a> <span class="p">{</span><span class="s2">"name"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"$regex"</span><span class="p">:</span> <span class="n">query</span><span class="p">,</span> <span class="s2">"$options"</span><span class="p">:</span> <span class="s2">"i"</span><span class="p">}}</span>
|
||||||
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a> <span class="p">)</span><span class="o">.</span><span class="n">to_list</span><span class="p">(</span><span class="n">length</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a> <span class="k">return</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="p">(</span><span class="o">**</span><span class="n">doc</span><span class="p">)</span> <span class="k">for</span> <span class="n">doc</span> <span class="ow">in</span> <span class="n">docs</span><span class="p">]</span>
|
||||||
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a>
|
||||||
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_by_category</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">category</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">in_stock_only</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="n">Product</span><span class="p">]:</span>
|
||||||
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="n">filter_query</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"category"</span><span class="p">:</span> <span class="n">category</span><span class="p">}</span>
|
||||||
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="k">if</span> <span class="n">in_stock_only</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="n">filter_query</span><span class="p">[</span><span class="s2">"in_stock"</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span>
|
||||||
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span><span class="nb">filter</span><span class="o">=</span><span class="n">filter_query</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a>
|
||||||
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_low_stock</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">threshold</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="n">Product</span><span class="p">]:</span>
|
||||||
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span><span class="nb">filter</span><span class="o">=</span><span class="p">{</span><span class="s2">"quantity"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"$lt"</span><span class="p">:</span> <span class="n">threshold</span><span class="p">},</span> <span class="s2">"in_stock"</span><span class="p">:</span> <span class="kc">True</span><span class="p">})</span>
|
||||||
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a>
|
||||||
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_stock</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">product_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">quantity_delta</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="n">Product</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a><span class="w"> </span><span class="sd">"""Atomically increment/decrement stock."""</span>
|
||||||
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
||||||
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="kn">from</span><span class="w"> </span><span class="nn">datetime</span><span class="w"> </span><span class="kn">import</span> <span class="n">datetime</span>
|
||||||
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a>
|
||||||
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">find_one_and_update</span><span class="p">(</span>
|
||||||
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="p">{</span><span class="s2">"_id"</span><span class="p">:</span> <span class="n">ObjectId</span><span class="p">(</span><span class="n">product_id</span><span class="p">)},</span>
|
||||||
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="p">{</span><span class="s2">"$inc"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"quantity"</span><span class="p">:</span> <span class="n">quantity_delta</span><span class="p">},</span> <span class="s2">"$set"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"updated_at"</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()}},</span>
|
||||||
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="n">return_document</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||||
|
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="p">(</span><span class="o">**</span><span class="n">result</span><span class="p">)</span> <span class="k">if</span> <span class="n">result</span> <span class="k">else</span> <span class="kc">None</span>
|
||||||
|
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a>
|
||||||
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a>
|
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a>
|
||||||
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/products/search"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">List</span><span class="p">[</span><span class="n">Product</span><span class="p">])</span>
|
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">search_products</span><span class="p">(</span><span class="n">q</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="o">...</span><span class="p">,</span> <span class="n">min_length</span><span class="o">=</span><span class="mi">1</span><span class="p">)):</span>
|
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a><span class="n">product_repo</span> <span class="o">=</span> <span class="n">ProductRepository</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">product_repo</span><span class="o">.</span><span class="n">search_by_name</span><span class="p">(</span><span class="n">q</span><span class="p">)</span>
|
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a>
|
||||||
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a>
|
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a>
|
||||||
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/products/category/</span><span class="si">{category}</span><span class="s2">"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">List</span><span class="p">[</span><span class="n">Product</span><span class="p">])</span>
|
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/products/search"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="nb">list</span><span class="p">[</span><span class="n">Product</span><span class="p">])</span>
|
||||||
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">products_by_category</span><span class="p">(</span><span class="n">category</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">in_stock</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">):</span>
|
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">search_products</span><span class="p">(</span><span class="n">q</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="o">...</span><span class="p">,</span> <span class="n">min_length</span><span class="o">=</span><span class="mi">1</span><span class="p">)):</span>
|
||||||
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">product_repo</span><span class="o">.</span><span class="n">get_by_category</span><span class="p">(</span><span class="n">category</span><span class="p">,</span> <span class="n">in_stock</span><span class="p">)</span>
|
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">product_repo</span><span class="o">.</span><span class="n">search_by_name</span><span class="p">(</span><span class="n">q</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a>
|
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a>
|
||||||
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/products/low-stock"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">List</span><span class="p">[</span><span class="n">Product</span><span class="p">])</span>
|
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a>
|
||||||
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">low_stock_products</span><span class="p">(</span><span class="n">threshold</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">):</span>
|
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/products/category/</span><span class="si">{category}</span><span class="s2">"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="nb">list</span><span class="p">[</span><span class="n">Product</span><span class="p">])</span>
|
||||||
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">product_repo</span><span class="o">.</span><span class="n">get_low_stock</span><span class="p">(</span><span class="n">threshold</span><span class="p">)</span>
|
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">products_by_category</span><span class="p">(</span><span class="n">category</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">in_stock</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a>
|
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">product_repo</span><span class="o">.</span><span class="n">get_by_category</span><span class="p">(</span><span class="n">category</span><span class="p">,</span> <span class="n">in_stock</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a><span class="nd">@app</span><span class="o">.</span><span class="n">patch</span><span class="p">(</span><span class="s2">"/products/</span><span class="si">{product_id}</span><span class="s2">/stock"</span><span class="p">)</span>
|
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a>
|
||||||
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_product_stock</span><span class="p">(</span><span class="n">product_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">quantity_delta</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
|
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a>
|
||||||
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a> <span class="n">product</span> <span class="o">=</span> <span class="k">await</span> <span class="n">product_repo</span><span class="o">.</span><span class="n">update_stock</span><span class="p">(</span><span class="n">product_id</span><span class="p">,</span> <span class="n">quantity_delta</span><span class="p">)</span>
|
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/products/low-stock"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="nb">list</span><span class="p">[</span><span class="n">Product</span><span class="p">])</span>
|
||||||
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">product</span><span class="p">:</span>
|
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">low_stock_products</span><span class="p">(</span><span class="n">threshold</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"Product not found"</span><span class="p">)</span>
|
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">product_repo</span><span class="o">.</span><span class="n">get_low_stock</span><span class="p">(</span><span class="n">threshold</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a> <span class="k">return</span> <span class="n">product</span>
|
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a>
|
||||||
|
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a>
|
||||||
|
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a><span class="nd">@app</span><span class="o">.</span><span class="n">patch</span><span class="p">(</span><span class="s2">"/products/</span><span class="si">{product_id}</span><span class="s2">/stock"</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_product_stock</span><span class="p">(</span><span class="n">product_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">quantity_delta</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a> <span class="n">product</span> <span class="o">=</span> <span class="k">await</span> <span class="n">product_repo</span><span class="o">.</span><span class="n">update_stock</span><span class="p">(</span><span class="n">product_id</span><span class="p">,</span> <span class="n">quantity_delta</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">product</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"Product not found"</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a> <span class="k">return</span> <span class="n">product</span>
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
|
<blockquote>
|
||||||
|
<p><strong>Note:</strong> this snippet omits the FastAPI <code>lifespan</code> connection wiring for brevity — copy it from <a href="../01_basic_crud/">use case 01</a> so <code>ProductRepository()</code> is created only after <code>MongoConnectionManager.connect()</code>.</p>
|
||||||
|
</blockquote>
|
||||||
|
<hr />
|
||||||
|
<h2 id="tips">💡 Tips</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Methods that hit <code>self.collection</code> directly (regex search, <code>$inc</code>) <strong>bypass</strong> the caching and population layers. If a feature composes them — extend <code>CachedBaseRepository</code> or <code>PopulatingRepository</code> instead and add the domain methods there.</li>
|
||||||
|
<li>Prefer <code>get_many(filter=...)</code> over raw <code>find</code> when you want pagination/sort defaults for free.</li>
|
||||||
|
<li>Reuse <code>self.model(**doc)</code> to convert raw dicts to model instances consistently.</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../01_basic_crud/">01 – Basic CRUD</a> · <a href="../04_pagination/">04 – Pagination</a> · <a href="../05_soft_deletes/">05 – Soft deletes</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -576,6 +576,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||||
|
|
||||||
|
|
||||||
@@ -608,6 +610,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__title" for="__toc">
|
<label class="md-nav__title" for="__toc">
|
||||||
<span class="md-nav__icon md-icon"></span>
|
<span class="md-nav__icon md-icon"></span>
|
||||||
Table of contents
|
Table of contents
|
||||||
@@ -615,9 +619,36 @@
|
|||||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#use-case-3-transaction-support-for-multi-document-operations" class="md-nav__link">
|
<a href="#whats-new" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Use Case 3: Transaction Support for Multi-Document Operations
|
📦 What's New?
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#example" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
🚀 Example
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#tips" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
💡 Tips
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -1004,71 +1035,129 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1>Transactions</h1>
|
<h1 id="use-case-3-transaction-support-for-multi-document-operations">Use Case 3: Transaction Support for Multi-Document Operations</h1>
|
||||||
|
<p><strong>Scenario:</strong> Order processing must update inventory and create an order <strong>atomically</strong>. Any failure rolls both back.</p>
|
||||||
<h3 id="use-case-3-transaction-support-for-multi-document-operations">Use Case 3: Transaction Support for Multi-Document Operations</h3>
|
<hr />
|
||||||
<p><strong>Scenario:</strong> Order processing system that updates inventory and creates order atomically.</p>
|
<h2 id="whats-new">📦 What's New?</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">BaseRepository</span><span class="p">,</span> <span class="n">TransactionManager</span>
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Component</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>TransactionManager.start_session</code></td>
|
||||||
|
<td>Async context manager yielding a session with an active transaction.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>session=</code> kwarg</td>
|
||||||
|
<td>Pass to every <code>insert_one</code> / <code>update_one</code> / <code>find_one</code> inside the block.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<hr />
|
||||||
|
<h2 id="example">🚀 Example</h2>
|
||||||
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">datetime</span><span class="w"> </span><span class="kn">import</span> <span class="n">datetime</span>
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">datetime</span><span class="w"> </span><span class="kn">import</span> <span class="n">datetime</span>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">List</span>
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">pydantic</span><span class="w"> </span><span class="kn">import</span> <span class="n">Field</span>
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a>
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span><span class="p">,</span> <span class="n">HTTPException</span>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="k">class</span><span class="w"> </span><span class="nc">Order</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">BaseRepository</span><span class="p">,</span> <span class="n">MongoConnectionManager</span><span class="p">,</span> <span class="n">TransactionManager</span>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a> <span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="n">items</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">dict</span><span class="p">]</span> <span class="c1"># [{"product_id": "...", "quantity": 2}]</span>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> <span class="n">total_amount</span><span class="p">:</span> <span class="nb">float</span>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="k">class</span><span class="w"> </span><span class="nc">Order</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="n">status</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"pending"</span>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a>
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> <span class="n">items</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">dict</span><span class="p">]</span> <span class="c1"># [{"product_id": "...", "quantity": 2}]</span>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a><span class="k">class</span><span class="w"> </span><span class="nc">OrderRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Order</span><span class="p">]):</span>
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> <span class="n">total_amount</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="mf">0.0</span>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">status</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"pending"</span>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"orders"</span><span class="p">,</span> <span class="n">Order</span><span class="p">)</span>
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a>
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a><span class="k">class</span><span class="w"> </span><span class="nc">OrderService</span><span class="p">:</span>
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a><span class="k">class</span><span class="w"> </span><span class="nc">Inventory</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">order_repo</span><span class="p">:</span> <span class="n">OrderRepository</span><span class="p">,</span> <span class="n">product_repo</span><span class="p">:</span> <span class="n">ProductRepository</span><span class="p">):</span>
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">product_id</span><span class="p">:</span> <span class="n">ObjectId</span> <span class="o">=</span> <span class="kc">None</span>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="bp">self</span><span class="o">.</span><span class="n">order_repo</span> <span class="o">=</span> <span class="n">order_repo</span>
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="n">quantity</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> <span class="bp">self</span><span class="o">.</span><span class="n">product_repo</span> <span class="o">=</span> <span class="n">product_repo</span>
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a>
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a>
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_order_with_inventory_update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">order</span><span class="p">:</span> <span class="n">Order</span><span class="p">)</span> <span class="o">-></span> <span class="n">Order</span><span class="p">:</span>
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="k">class</span><span class="w"> </span><span class="nc">OrderRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Order</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="w"> </span><span class="sd">"""Create order and update inventory atomically"""</span>
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a>
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"orders"</span><span class="p">,</span> <span class="n">Order</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_order_transaction</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a>
|
||||||
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="c1"># Insert order</span>
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a>
|
||||||
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> <span class="n">order_doc</span> <span class="o">=</span> <span class="n">order</span><span class="o">.</span><span class="n">model_dump</span><span class="p">(</span><span class="n">exclude</span><span class="o">=</span><span class="p">{</span><span class="s2">"id"</span><span class="p">},</span> <span class="n">exclude_none</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a><span class="k">class</span><span class="w"> </span><span class="nc">InventoryRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Inventory</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a> <span class="n">order_doc</span><span class="p">[</span><span class="s2">"created_at"</span><span class="p">]</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()</span>
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a> <span class="n">order_doc</span><span class="p">[</span><span class="s2">"updated_at"</span><span class="p">]</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()</span>
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"inventory"</span><span class="p">,</span> <span class="n">Inventory</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">order_repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">insert_one</span><span class="p">(</span><span class="n">order_doc</span><span class="p">,</span> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">)</span>
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a>
|
||||||
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a>
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a>
|
||||||
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a> <span class="c1"># Update inventory for each item</span>
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_order_with_inventory_update</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">order</span><span class="o">.</span><span class="n">items</span><span class="p">:</span>
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="n">order</span><span class="p">:</span> <span class="n">Order</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">product_repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">update_one</span><span class="p">(</span>
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="n">order_repo</span><span class="p">:</span> <span class="n">OrderRepository</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="p">{</span><span class="s2">"_id"</span><span class="p">:</span> <span class="n">ObjectId</span><span class="p">(</span><span class="n">item</span><span class="p">[</span><span class="s2">"product_id"</span><span class="p">])},</span>
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="n">inv_repo</span><span class="p">:</span> <span class="n">InventoryRepository</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="p">{</span>
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a><span class="p">)</span> <span class="o">-></span> <span class="n">Order</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="s2">"$inc"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"quantity"</span><span class="p">:</span> <span class="o">-</span><span class="n">item</span><span class="p">[</span><span class="s2">"quantity"</span><span class="p">]},</span>
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">insert_order</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="s2">"$set"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"updated_at"</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()}</span>
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="n">doc</span> <span class="o">=</span> <span class="n">order</span><span class="o">.</span><span class="n">model_dump</span><span class="p">(</span><span class="n">exclude</span><span class="o">=</span><span class="p">{</span><span class="s2">"id"</span><span class="p">},</span> <span class="n">exclude_none</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="p">},</span>
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="n">doc</span><span class="p">[</span><span class="s2">"created_at"</span><span class="p">]</span> <span class="o">=</span> <span class="n">doc</span><span class="p">[</span><span class="s2">"updated_at"</span><span class="p">]</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> <span class="n">session</span><span class="o">=</span><span class="n">session</span>
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">order_repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">insert_one</span><span class="p">(</span><span class="n">doc</span><span class="p">,</span> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="p">)</span>
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">order_repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">find_one</span><span class="p">({</span><span class="s2">"_id"</span><span class="p">:</span> <span class="n">result</span><span class="o">.</span><span class="n">inserted_id</span><span class="p">},</span> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a>
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a>
|
||||||
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="c1"># Fetch created order</span>
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_inventory</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="n">created</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">order_repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">find_one</span><span class="p">(</span>
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">order</span><span class="o">.</span><span class="n">items</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="p">{</span><span class="s2">"_id"</span><span class="p">:</span> <span class="n">result</span><span class="o">.</span><span class="n">inserted_id</span><span class="p">},</span>
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="k">await</span> <span class="n">inv_repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">update_one</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="n">session</span><span class="o">=</span><span class="n">session</span>
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="p">{</span><span class="s2">"product_id"</span><span class="p">:</span> <span class="n">ObjectId</span><span class="p">(</span><span class="n">item</span><span class="p">[</span><span class="s2">"product_id"</span><span class="p">])},</span>
|
||||||
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="p">)</span>
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="p">{</span>
|
||||||
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="k">return</span> <span class="n">Order</span><span class="p">(</span><span class="o">**</span><span class="n">created</span><span class="p">)</span>
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="s2">"$inc"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"quantity"</span><span class="p">:</span> <span class="o">-</span><span class="n">item</span><span class="p">[</span><span class="s2">"quantity"</span><span class="p">]},</span>
|
||||||
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a>
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="s2">"$set"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"updated_at"</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()},</span>
|
||||||
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="c1"># Execute in transaction</span>
|
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="p">},</span>
|
||||||
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">TransactionManager</span><span class="o">.</span><span class="n">start_session</span><span class="p">()</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">create_order_transaction</span><span class="p">(</span><span class="n">session</span><span class="p">)</span>
|
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a> <span class="p">)</span>
|
||||||
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a>
|
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a> <span class="k">return</span> <span class="kc">None</span>
|
||||||
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a><span class="c1"># Usage in FastAPI</span>
|
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a>
|
||||||
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/orders/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">Order</span><span class="p">)</span>
|
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">TransactionManager</span><span class="o">.</span><span class="n">start_session</span><span class="p">()</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_order</span><span class="p">(</span><span class="n">order</span><span class="p">:</span> <span class="n">Order</span><span class="p">):</span>
|
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="n">created</span> <span class="o">=</span> <span class="k">await</span> <span class="n">insert_order</span><span class="p">(</span><span class="n">session</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a> <span class="n">order_service</span> <span class="o">=</span> <span class="n">OrderService</span><span class="p">(</span><span class="n">order_repo</span><span class="p">,</span> <span class="n">product_repo</span><span class="p">)</span>
|
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a> <span class="k">await</span> <span class="n">update_inventory</span><span class="p">(</span><span class="n">session</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a> <span class="k">try</span><span class="p">:</span>
|
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a> <span class="k">return</span> <span class="n">Order</span><span class="p">(</span><span class="o">**</span><span class="n">created</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">order_service</span><span class="o">.</span><span class="n">create_order_with_inventory_update</span><span class="p">(</span><span class="n">order</span><span class="p">)</span>
|
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a>
|
||||||
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a>
|
||||||
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
|
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a><span class="c1"># ---------------------------</span>
|
||||||
|
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a><span class="c1"># FastAPI wiring</span>
|
||||||
|
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a><span class="c1"># ---------------------------</span>
|
||||||
|
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">()</span>
|
||||||
|
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a>
|
||||||
|
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a><span class="n">order_repo</span> <span class="o">=</span> <span class="n">OrderRepository</span><span class="p">()</span>
|
||||||
|
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a><span class="n">inv_repo</span> <span class="o">=</span> <span class="n">InventoryRepository</span><span class="p">()</span>
|
||||||
|
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a>
|
||||||
|
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a>
|
||||||
|
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a><span class="nd">@asynccontextmanager</span>
|
||||||
|
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">_app</span><span class="p">:</span> <span class="n">FastAPI</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
||||||
|
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span> <span class="n">db_name</span><span class="o">=</span><span class="s2">"shop"</span>
|
||||||
|
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a> <span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a> <span class="k">yield</span>
|
||||||
|
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a>
|
||||||
|
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a>
|
||||||
|
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a><span class="n">app</span><span class="o">.</span><span class="n">lifespan</span> <span class="o">=</span> <span class="n">lifespan</span>
|
||||||
|
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a>
|
||||||
|
<a id="__codelineno-0-79" name="__codelineno-0-79" href="#__codelineno-0-79"></a>
|
||||||
|
<a id="__codelineno-0-80" name="__codelineno-0-80" href="#__codelineno-0-80"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/orders/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">Order</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-81" name="__codelineno-0-81" href="#__codelineno-0-81"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_order</span><span class="p">(</span><span class="n">order</span><span class="p">:</span> <span class="n">Order</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-82" name="__codelineno-0-82" href="#__codelineno-0-82"></a> <span class="k">try</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-83" name="__codelineno-0-83" href="#__codelineno-0-83"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">create_order_with_inventory_update</span><span class="p">(</span><span class="n">order</span><span class="p">,</span> <span class="n">order_repo</span><span class="p">,</span> <span class="n">inv_repo</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-84" name="__codelineno-0-84" href="#__codelineno-0-84"></a> <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">exc</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-85" name="__codelineno-0-85" href="#__codelineno-0-85"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">exc</span><span class="p">))</span>
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
|
<hr />
|
||||||
|
<h2 id="tips">💡 Tips</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Repositories are created <strong>inside</strong> the lifespan (or a dependency) so the database is connected — see the note in <a href="../01_basic_crud/">use case 01</a>.</li>
|
||||||
|
<li>Every operation inside the transaction context must receive <code>session=<session></code> — a missing session silently runs outside the transaction.</li>
|
||||||
|
<li>MongoDB transactions require a replica set (or the local standalone test server that emulates one).</li>
|
||||||
|
<li>For a list-of-operations style, see <code>TransactionManager.execute_transaction</code> in <a href="../12_transaction_helper/">use case 12</a>.</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../06_multi_model/">06 – Multi-model</a> · <a href="../12_transaction_helper/">12 – Transaction helper</a> · <a href="../../06_error_handling/">Error Handling</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -599,6 +599,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||||
|
|
||||||
|
|
||||||
@@ -631,6 +633,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__title" for="__toc">
|
<label class="md-nav__title" for="__toc">
|
||||||
<span class="md-nav__icon md-icon"></span>
|
<span class="md-nav__icon md-icon"></span>
|
||||||
Table of contents
|
Table of contents
|
||||||
@@ -638,9 +642,36 @@
|
|||||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#use-case-4-pagination-filtering" class="md-nav__link">
|
<a href="#whats-new" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Use Case 4: Pagination & Filtering
|
📦 What's New?
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#example" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
🚀 Example
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#tips" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
💡 Tips
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -1004,107 +1035,147 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1>Pagination</h1>
|
<h1 id="use-case-4-pagination-filtering">Use Case 4: Pagination & Filtering</h1>
|
||||||
|
<p><strong>Scenario:</strong> A blog post API lists posts with page metadata, filtering, and sorting.</p>
|
||||||
<h3 id="use-case-4-pagination-filtering">Use Case 4: Pagination & Filtering</h3>
|
<hr />
|
||||||
<p><strong>Scenario:</strong> Blog post API with pagination and filtering.</p>
|
<h2 id="whats-new">📦 What's New?</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">BaseRepository</span>
|
<table>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">pydantic</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">Field</span>
|
<thead>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Generic</span><span class="p">,</span> <span class="n">TypeVar</span>
|
<tr>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a>
|
<th>Component</th>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">"T"</span><span class="p">)</span>
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>BaseRepository.get_many</code></td>
|
||||||
|
<td><code>filter</code>, <code>skip</code>, <code>limit</code>, <code>sort</code> in one call.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>BaseRepository.count</code></td>
|
||||||
|
<td>Total matching documents for page metadata.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>PaginatedResponse</code></td>
|
||||||
|
<td>Reusable generic response wrapper.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<hr />
|
||||||
|
<h2 id="example">🚀 Example</h2>
|
||||||
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Generic</span><span class="p">,</span> <span class="n">TypeVar</span>
|
||||||
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>
|
||||||
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span><span class="p">,</span> <span class="n">Query</span>
|
||||||
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">pydantic</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseModel</span>
|
||||||
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">BaseRepository</span>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="k">class</span><span class="w"> </span><span class="nc">PaginatedResponse</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">,</span> <span class="n">Generic</span><span class="p">[</span><span class="n">T</span><span class="p">]):</span>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">"T"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="n">items</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">T</span><span class="p">]</span>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> <span class="n">total</span><span class="p">:</span> <span class="nb">int</span>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="n">page</span><span class="p">:</span> <span class="nb">int</span>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a><span class="k">class</span><span class="w"> </span><span class="nc">PaginatedResponse</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">,</span> <span class="n">Generic</span><span class="p">[</span><span class="n">T</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> <span class="n">page_size</span><span class="p">:</span> <span class="nb">int</span>
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> <span class="n">items</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">T</span><span class="p">]</span>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> <span class="n">total_pages</span><span class="p">:</span> <span class="nb">int</span>
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> <span class="n">total</span><span class="p">:</span> <span class="nb">int</span>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">has_next</span><span class="p">:</span> <span class="nb">bool</span>
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">page</span><span class="p">:</span> <span class="nb">int</span>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">has_prev</span><span class="p">:</span> <span class="nb">bool</span>
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">page_size</span><span class="p">:</span> <span class="nb">int</span>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a>
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="n">total_pages</span><span class="p">:</span> <span class="nb">int</span>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a><span class="k">class</span><span class="w"> </span><span class="nc">BlogPost</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="n">has_next</span><span class="p">:</span> <span class="nb">bool</span>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span>
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">has_prev</span><span class="p">:</span> <span class="nb">bool</span>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="n">content</span><span class="p">:</span> <span class="nb">str</span>
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> <span class="n">author_id</span><span class="p">:</span> <span class="nb">str</span>
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> <span class="n">published</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a><span class="k">class</span><span class="w"> </span><span class="nc">BlogPost</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> <span class="n">tags</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="n">views</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="n">content</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a>
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="n">author_id</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a><span class="k">class</span><span class="w"> </span><span class="nc">BlogPostRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">BlogPost</span><span class="p">]):</span>
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="n">published</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
||||||
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="n">tags</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
||||||
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"blog_posts"</span><span class="p">,</span> <span class="n">BlogPost</span><span class="p">)</span>
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> <span class="n">views</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
|
||||||
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a>
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a>
|
||||||
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">paginate</span><span class="p">(</span>
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a>
|
||||||
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a> <span class="bp">self</span><span class="p">,</span>
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a><span class="k">class</span><span class="w"> </span><span class="nc">BlogPostRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">BlogPost</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a> <span class="n">page</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a> <span class="n">page_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"blog_posts"</span><span class="p">,</span> <span class="n">BlogPost</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="n">filter_dict</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">dict</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a>
|
||||||
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="n">sort_by</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"created_at"</span><span class="p">,</span>
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">paginate</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="n">sort_order</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="bp">self</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="p">)</span> <span class="o">-></span> <span class="n">PaginatedResponse</span><span class="p">[</span><span class="n">BlogPost</span><span class="p">]:</span>
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="n">page</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a><span class="w"> </span><span class="sd">"""Get paginated blog posts"""</span>
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="n">page_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="n">filter_dict</span> <span class="o">=</span> <span class="n">filter_dict</span> <span class="ow">or</span> <span class="p">{}</span>
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="n">filter_dict</span><span class="p">:</span> <span class="nb">dict</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="n">skip</span> <span class="o">=</span> <span class="p">(</span><span class="n">page</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="n">page_size</span>
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="n">sort_by</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"created_at"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a>
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> <span class="n">sort_order</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="c1"># Get total count</span>
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="p">)</span> <span class="o">-></span> <span class="n">PaginatedResponse</span><span class="p">[</span><span class="n">BlogPost</span><span class="p">]:</span>
|
||||||
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="n">total</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">count</span><span class="p">(</span><span class="n">filter_dict</span><span class="p">)</span>
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="n">filter_dict</span> <span class="o">=</span> <span class="n">filter_dict</span> <span class="ow">or</span> <span class="p">{}</span>
|
||||||
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a>
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="n">skip</span> <span class="o">=</span> <span class="p">(</span><span class="n">page</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="n">page_size</span>
|
||||||
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="c1"># Get items</span>
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="n">total</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">count</span><span class="p">(</span><span class="n">filter_dict</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="n">items</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span>
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="n">items</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="nb">filter</span><span class="o">=</span><span class="n">filter_dict</span><span class="p">,</span>
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="nb">filter</span><span class="o">=</span><span class="n">filter_dict</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="n">skip</span><span class="o">=</span><span class="n">skip</span><span class="p">,</span>
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="n">skip</span><span class="o">=</span><span class="n">skip</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="n">limit</span><span class="o">=</span><span class="n">page_size</span><span class="p">,</span>
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="n">limit</span><span class="o">=</span><span class="n">page_size</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="n">sort</span><span class="o">=</span><span class="p">[(</span><span class="n">sort_by</span><span class="p">,</span> <span class="n">sort_order</span><span class="p">)]</span>
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="n">sort</span><span class="o">=</span><span class="p">[(</span><span class="n">sort_by</span><span class="p">,</span> <span class="n">sort_order</span><span class="p">)],</span>
|
||||||
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="p">)</span>
|
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="p">)</span>
|
||||||
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a>
|
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="n">total_pages</span> <span class="o">=</span> <span class="p">(</span><span class="n">total</span> <span class="o">+</span> <span class="n">page_size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">//</span> <span class="n">page_size</span>
|
||||||
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a> <span class="n">total_pages</span> <span class="o">=</span> <span class="p">(</span><span class="n">total</span> <span class="o">+</span> <span class="n">page_size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">//</span> <span class="n">page_size</span>
|
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a> <span class="k">return</span> <span class="n">PaginatedResponse</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a>
|
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a> <span class="n">items</span><span class="o">=</span><span class="n">items</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a> <span class="k">return</span> <span class="n">PaginatedResponse</span><span class="p">(</span>
|
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a> <span class="n">total</span><span class="o">=</span><span class="n">total</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="n">items</span><span class="o">=</span><span class="n">items</span><span class="p">,</span>
|
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="n">page</span><span class="o">=</span><span class="n">page</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="n">total</span><span class="o">=</span><span class="n">total</span><span class="p">,</span>
|
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="n">page_size</span><span class="o">=</span><span class="n">page_size</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a> <span class="n">page</span><span class="o">=</span><span class="n">page</span><span class="p">,</span>
|
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a> <span class="n">total_pages</span><span class="o">=</span><span class="n">total_pages</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a> <span class="n">page_size</span><span class="o">=</span><span class="n">page_size</span><span class="p">,</span>
|
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a> <span class="n">has_next</span><span class="o">=</span><span class="n">page</span> <span class="o"><</span> <span class="n">total_pages</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> <span class="n">total_pages</span><span class="o">=</span><span class="n">total_pages</span><span class="p">,</span>
|
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> <span class="n">has_prev</span><span class="o">=</span><span class="n">page</span> <span class="o">></span> <span class="mi">1</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> <span class="n">has_next</span><span class="o">=</span><span class="n">page</span> <span class="o"><</span> <span class="n">total_pages</span><span class="p">,</span>
|
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> <span class="p">)</span>
|
||||||
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a> <span class="n">has_prev</span><span class="o">=</span><span class="n">page</span> <span class="o">></span> <span class="mi">1</span>
|
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a>
|
||||||
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a> <span class="p">)</span>
|
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_by_author</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">author_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">published_only</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="n">BlogPost</span><span class="p">]:</span>
|
||||||
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a>
|
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a> <span class="n">filter_dict</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"author_id"</span><span class="p">:</span> <span class="n">author_id</span><span class="p">}</span>
|
||||||
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_by_author</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">author_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">published_only</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">):</span>
|
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a> <span class="k">if</span> <span class="n">published_only</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a><span class="w"> </span><span class="sd">"""Get posts by author"""</span>
|
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a> <span class="n">filter_dict</span><span class="p">[</span><span class="s2">"published"</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span>
|
||||||
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a> <span class="n">filter_dict</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"author_id"</span><span class="p">:</span> <span class="n">author_id</span><span class="p">}</span>
|
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span><span class="nb">filter</span><span class="o">=</span><span class="n">filter_dict</span><span class="p">,</span> <span class="n">sort</span><span class="o">=</span><span class="p">[(</span><span class="s2">"created_at"</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)])</span>
|
||||||
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a> <span class="k">if</span> <span class="n">published_only</span><span class="p">:</span>
|
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a>
|
||||||
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a> <span class="n">filter_dict</span><span class="p">[</span><span class="s2">"published"</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span>
|
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">search_by_tags</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">tags</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="n">BlogPost</span><span class="p">]:</span>
|
||||||
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span><span class="nb">filter</span><span class="o">=</span><span class="n">filter_dict</span><span class="p">,</span> <span class="n">sort</span><span class="o">=</span><span class="p">[(</span><span class="s2">"created_at"</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)])</span>
|
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span><span class="nb">filter</span><span class="o">=</span><span class="p">{</span><span class="s2">"tags"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"$in"</span><span class="p">:</span> <span class="n">tags</span><span class="p">},</span> <span class="s2">"published"</span><span class="p">:</span> <span class="kc">True</span><span class="p">})</span>
|
||||||
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a>
|
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a>
|
||||||
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">search_by_tags</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">tags</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="n">BlogPost</span><span class="p">]:</span>
|
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a>
|
||||||
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a><span class="w"> </span><span class="sd">"""Search posts by tags"""</span>
|
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span><span class="nb">filter</span><span class="o">=</span><span class="p">{</span><span class="s2">"tags"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"$in"</span><span class="p">:</span> <span class="n">tags</span><span class="p">},</span> <span class="s2">"published"</span><span class="p">:</span> <span class="kc">True</span><span class="p">})</span>
|
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a><span class="n">blog_repo</span> <span class="o">=</span> <span class="n">BlogPostRepository</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a>
|
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a>
|
||||||
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a><span class="c1"># Usage in FastAPI</span>
|
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a>
|
||||||
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/posts/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">PaginatedResponse</span><span class="p">[</span><span class="n">BlogPost</span><span class="p">])</span>
|
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/posts/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">PaginatedResponse</span><span class="p">[</span><span class="n">BlogPost</span><span class="p">])</span>
|
||||||
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_posts</span><span class="p">(</span>
|
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_posts</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a> <span class="n">page</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
|
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a> <span class="n">page</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a> <span class="n">page_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
|
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a> <span class="n">page_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-79" name="__codelineno-0-79" href="#__codelineno-0-79"></a> <span class="n">published</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
<a id="__codelineno-0-79" name="__codelineno-0-79" href="#__codelineno-0-79"></a> <span class="n">published</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-80" name="__codelineno-0-80" href="#__codelineno-0-80"></a> <span class="n">author_id</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
<a id="__codelineno-0-80" name="__codelineno-0-80" href="#__codelineno-0-80"></a> <span class="n">author_id</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-81" name="__codelineno-0-81" href="#__codelineno-0-81"></a><span class="p">):</span>
|
<a id="__codelineno-0-81" name="__codelineno-0-81" href="#__codelineno-0-81"></a><span class="p">):</span>
|
||||||
<a id="__codelineno-0-82" name="__codelineno-0-82" href="#__codelineno-0-82"></a> <span class="n">filter_dict</span> <span class="o">=</span> <span class="p">{}</span>
|
<a id="__codelineno-0-82" name="__codelineno-0-82" href="#__codelineno-0-82"></a> <span class="n">filter_dict</span> <span class="o">=</span> <span class="p">{}</span>
|
||||||
<a id="__codelineno-0-83" name="__codelineno-0-83" href="#__codelineno-0-83"></a> <span class="k">if</span> <span class="n">published</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
<a id="__codelineno-0-83" name="__codelineno-0-83" href="#__codelineno-0-83"></a> <span class="k">if</span> <span class="n">published</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-84" name="__codelineno-0-84" href="#__codelineno-0-84"></a> <span class="n">filter_dict</span><span class="p">[</span><span class="s2">"published"</span><span class="p">]</span> <span class="o">=</span> <span class="n">published</span>
|
<a id="__codelineno-0-84" name="__codelineno-0-84" href="#__codelineno-0-84"></a> <span class="n">filter_dict</span><span class="p">[</span><span class="s2">"published"</span><span class="p">]</span> <span class="o">=</span> <span class="n">published</span>
|
||||||
<a id="__codelineno-0-85" name="__codelineno-0-85" href="#__codelineno-0-85"></a> <span class="k">if</span> <span class="n">author_id</span><span class="p">:</span>
|
<a id="__codelineno-0-85" name="__codelineno-0-85" href="#__codelineno-0-85"></a> <span class="k">if</span> <span class="n">author_id</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-86" name="__codelineno-0-86" href="#__codelineno-0-86"></a> <span class="n">filter_dict</span><span class="p">[</span><span class="s2">"author_id"</span><span class="p">]</span> <span class="o">=</span> <span class="n">author_id</span>
|
<a id="__codelineno-0-86" name="__codelineno-0-86" href="#__codelineno-0-86"></a> <span class="n">filter_dict</span><span class="p">[</span><span class="s2">"author_id"</span><span class="p">]</span> <span class="o">=</span> <span class="n">author_id</span>
|
||||||
<a id="__codelineno-0-87" name="__codelineno-0-87" href="#__codelineno-0-87"></a>
|
<a id="__codelineno-0-87" name="__codelineno-0-87" href="#__codelineno-0-87"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">blog_repo</span><span class="o">.</span><span class="n">paginate</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">page_size</span><span class="p">,</span> <span class="n">filter_dict</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-88" name="__codelineno-0-88" href="#__codelineno-0-88"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">blog_repo</span><span class="o">.</span><span class="n">paginate</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">page_size</span><span class="p">,</span> <span class="n">filter_dict</span><span class="p">)</span>
|
<a id="__codelineno-0-88" name="__codelineno-0-88" href="#__codelineno-0-88"></a>
|
||||||
<a id="__codelineno-0-89" name="__codelineno-0-89" href="#__codelineno-0-89"></a>
|
<a id="__codelineno-0-89" name="__codelineno-0-89" href="#__codelineno-0-89"></a>
|
||||||
<a id="__codelineno-0-90" name="__codelineno-0-90" href="#__codelineno-0-90"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/posts/author/</span><span class="si">{author_id}</span><span class="s2">"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">List</span><span class="p">[</span><span class="n">BlogPost</span><span class="p">])</span>
|
<a id="__codelineno-0-90" name="__codelineno-0-90" href="#__codelineno-0-90"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/posts/author/</span><span class="si">{author_id}</span><span class="s2">"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="nb">list</span><span class="p">[</span><span class="n">BlogPost</span><span class="p">])</span>
|
||||||
<a id="__codelineno-0-91" name="__codelineno-0-91" href="#__codelineno-0-91"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">posts_by_author</span><span class="p">(</span><span class="n">author_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">published</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">):</span>
|
<a id="__codelineno-0-91" name="__codelineno-0-91" href="#__codelineno-0-91"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">posts_by_author</span><span class="p">(</span><span class="n">author_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">published</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-92" name="__codelineno-0-92" href="#__codelineno-0-92"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">blog_repo</span><span class="o">.</span><span class="n">get_by_author</span><span class="p">(</span><span class="n">author_id</span><span class="p">,</span> <span class="n">published</span><span class="p">)</span>
|
<a id="__codelineno-0-92" name="__codelineno-0-92" href="#__codelineno-0-92"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">blog_repo</span><span class="o">.</span><span class="n">get_by_author</span><span class="p">(</span><span class="n">author_id</span><span class="p">,</span> <span class="n">published</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-93" name="__codelineno-0-93" href="#__codelineno-0-93"></a>
|
<a id="__codelineno-0-93" name="__codelineno-0-93" href="#__codelineno-0-93"></a>
|
||||||
<a id="__codelineno-0-94" name="__codelineno-0-94" href="#__codelineno-0-94"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/posts/tags"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">List</span><span class="p">[</span><span class="n">BlogPost</span><span class="p">])</span>
|
<a id="__codelineno-0-94" name="__codelineno-0-94" href="#__codelineno-0-94"></a>
|
||||||
<a id="__codelineno-0-95" name="__codelineno-0-95" href="#__codelineno-0-95"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">posts_by_tags</span><span class="p">(</span><span class="n">tags</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="o">...</span><span class="p">)):</span>
|
<a id="__codelineno-0-95" name="__codelineno-0-95" href="#__codelineno-0-95"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/posts/tags"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="nb">list</span><span class="p">[</span><span class="n">BlogPost</span><span class="p">])</span>
|
||||||
<a id="__codelineno-0-96" name="__codelineno-0-96" href="#__codelineno-0-96"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">blog_repo</span><span class="o">.</span><span class="n">search_by_tags</span><span class="p">(</span><span class="n">tags</span><span class="p">)</span>
|
<a id="__codelineno-0-96" name="__codelineno-0-96" href="#__codelineno-0-96"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">posts_by_tags</span><span class="p">(</span><span class="n">tags</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="o">...</span><span class="p">)):</span>
|
||||||
|
<a id="__codelineno-0-97" name="__codelineno-0-97" href="#__codelineno-0-97"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">blog_repo</span><span class="o">.</span><span class="n">search_by_tags</span><span class="p">(</span><span class="n">tags</span><span class="p">)</span>
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
|
<blockquote>
|
||||||
|
<p><strong>Note:</strong> add the <code>lifespan</code> wiring from <a href="../01_basic_crud/">use case 01</a> so <code>blog_repo</code> is created after connection.</p>
|
||||||
|
</blockquote>
|
||||||
|
<hr />
|
||||||
|
<h2 id="tips">💡 Tips</h2>
|
||||||
|
<ul>
|
||||||
|
<li><code>get_many</code> calls <code>cursor.to_list(limit)</code>; pass <code>limit=0</code> to skip the limit entirely, otherwise a large explicit limit is safer than unbounded reads.</li>
|
||||||
|
<li>Always <code>count</code> first for stable metadata — it uses the same filter as the page query.</li>
|
||||||
|
<li>Combine with <code>sort=[(field, -1|1)]</code> for deterministic ordering; create matching indexes to avoid full collection scans (see <a href="../13_index_creation/">use case 13</a>).</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../02_custom_repo/">02 – Custom repository</a> · <a href="../13_index_creation/">13 – Indexes</a> · <a href="../05_soft_deletes/">05 – Soft deletes</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -622,6 +622,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||||
|
|
||||||
|
|
||||||
@@ -654,6 +656,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__title" for="__toc">
|
<label class="md-nav__title" for="__toc">
|
||||||
<span class="md-nav__icon md-icon"></span>
|
<span class="md-nav__icon md-icon"></span>
|
||||||
Table of contents
|
Table of contents
|
||||||
@@ -661,9 +665,36 @@
|
|||||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#use-case-5-soft-deletes-pattern" class="md-nav__link">
|
<a href="#whats-new" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Use Case 5: Soft Deletes Pattern
|
📦 What's New?
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#example" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
🚀 Example
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#tips" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
💡 Tips
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -1004,93 +1035,125 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1>Soft Deletes</h1>
|
<h1 id="use-case-5-soft-deletes-pattern">Use Case 5: Soft Deletes Pattern</h1>
|
||||||
|
<p><strong>Scenario:</strong> Deleting a task should be recoverable. Instead of removing the document, set a tombstone flag and filter it from normal queries.</p>
|
||||||
<h3 id="use-case-5-soft-deletes-pattern">Use Case 5: Soft Deletes Pattern</h3>
|
<hr />
|
||||||
<p><strong>Scenario:</strong> Implement soft delete functionality for data recovery.</p>
|
<h2 id="whats-new">📦 What's New?</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">BaseRepository</span>
|
<table>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">datetime</span><span class="w"> </span><span class="kn">import</span> <span class="n">datetime</span>
|
<thead>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Optional</span>
|
<tr>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a>
|
<th>Component</th>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="k">class</span><span class="w"> </span><span class="nc">SoftDeleteDocument</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<th>Description</th>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="w"> </span><span class="sd">"""Base document with soft delete support"""</span>
|
</tr>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a> <span class="n">is_deleted</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
</thead>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="n">deleted_at</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">datetime</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
<tbody>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> <span class="n">deleted_by</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
<tr>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a>
|
<td><code>SoftDeleteDocument</code></td>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a><span class="k">class</span><span class="w"> </span><span class="nc">Task</span><span class="p">(</span><span class="n">SoftDeleteDocument</span><span class="p">):</span>
|
<td>Base model carrying <code>is_deleted</code>, <code>deleted_at</code>, <code>deleted_by</code>.</td>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span>
|
</tr>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">description</span><span class="p">:</span> <span class="nb">str</span>
|
<tr>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">assignee_id</span><span class="p">:</span> <span class="nb">str</span>
|
<td><code>SoftDeleteRepository[T]</code></td>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="n">status</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"pending"</span>
|
<td>Repository-level soft delete / restore / filtering using <code>update</code> + <code>get_many</code>.</td>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="n">priority</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"medium"</span>
|
</tr>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a>
|
<tr>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a><span class="k">class</span><span class="w"> </span><span class="nc">SoftDeleteRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">T</span><span class="p">]):</span>
|
<td><code>permanent_delete</code></td>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a><span class="w"> </span><span class="sd">"""Repository with soft delete operations"""</span>
|
<td>Escapes to the real <code>delete</code>.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<hr />
|
||||||
|
<h2 id="example">🚀 Example</h2>
|
||||||
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">datetime</span><span class="w"> </span><span class="kn">import</span> <span class="n">datetime</span>
|
||||||
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>
|
||||||
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span><span class="p">,</span> <span class="n">HTTPException</span>
|
||||||
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">BaseRepository</span>
|
||||||
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a>
|
||||||
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>
|
||||||
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="k">class</span><span class="w"> </span><span class="nc">SoftDeleteDocument</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="n">is_deleted</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
||||||
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> <span class="n">deleted_at</span><span class="p">:</span> <span class="n">datetime</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||||||
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="n">deleted_by</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||||||
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a>
|
||||||
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a>
|
||||||
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a><span class="k">class</span><span class="w"> </span><span class="nc">Task</span><span class="p">(</span><span class="n">SoftDeleteDocument</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="n">description</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="n">assignee_id</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">status</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"pending"</span>
|
||||||
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="n">priority</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"medium"</span>
|
||||||
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a>
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a>
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">soft_delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">deleted_by</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="n">T</span><span class="p">]:</span>
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="k">class</span><span class="w"> </span><span class="nc">SoftDeleteRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">T</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="w"> </span><span class="sd">"""Soft delete a document"""</span>
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">soft_delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">deleted_by</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="p">{</span>
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">update</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="s2">"is_deleted"</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="nb">id</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="s2">"deleted_at"</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">(),</span>
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="p">{</span><span class="s2">"is_deleted"</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s2">"deleted_at"</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">(),</span> <span class="s2">"deleted_by"</span><span class="p">:</span> <span class="n">deleted_by</span><span class="p">},</span>
|
||||||
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> <span class="s2">"deleted_by"</span><span class="p">:</span> <span class="n">deleted_by</span>
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> <span class="p">)</span>
|
||||||
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a> <span class="p">})</span>
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a>
|
||||||
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a>
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">restore</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">restore</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="n">T</span><span class="p">]:</span>
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">update</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a><span class="w"> </span><span class="sd">"""Restore a soft-deleted document"""</span>
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a> <span class="nb">id</span><span class="p">,</span> <span class="p">{</span><span class="s2">"is_deleted"</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s2">"deleted_at"</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="s2">"deleted_by"</span><span class="p">:</span> <span class="kc">None</span><span class="p">}</span>
|
||||||
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="p">{</span>
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a> <span class="p">)</span>
|
||||||
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="s2">"is_deleted"</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span>
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a>
|
||||||
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="s2">"deleted_at"</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_active</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">skip</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">100</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="s2">"deleted_by"</span><span class="p">:</span> <span class="kc">None</span>
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span><span class="nb">filter</span><span class="o">=</span><span class="p">{</span><span class="s2">"is_deleted"</span><span class="p">:</span> <span class="kc">False</span><span class="p">},</span> <span class="n">skip</span><span class="o">=</span><span class="n">skip</span><span class="p">,</span> <span class="n">limit</span><span class="o">=</span><span class="n">limit</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="p">})</span>
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a>
|
||||||
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a>
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_deleted</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">skip</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">100</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_active</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">skip</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">100</span><span class="p">):</span>
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span><span class="nb">filter</span><span class="o">=</span><span class="p">{</span><span class="s2">"is_deleted"</span><span class="p">:</span> <span class="kc">True</span><span class="p">},</span> <span class="n">skip</span><span class="o">=</span><span class="n">skip</span><span class="p">,</span> <span class="n">limit</span><span class="o">=</span><span class="n">limit</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a><span class="w"> </span><span class="sd">"""Get only non-deleted documents"""</span>
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a>
|
||||||
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span>
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">permanent_delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="nb">filter</span><span class="o">=</span><span class="p">{</span><span class="s2">"is_deleted"</span><span class="p">:</span> <span class="kc">False</span><span class="p">},</span>
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="n">skip</span><span class="o">=</span><span class="n">skip</span><span class="p">,</span>
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a>
|
||||||
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="n">limit</span><span class="o">=</span><span class="n">limit</span>
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a>
|
||||||
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="p">)</span>
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a><span class="k">class</span><span class="w"> </span><span class="nc">TaskRepository</span><span class="p">(</span><span class="n">SoftDeleteRepository</span><span class="p">[</span><span class="n">Task</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a>
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_deleted</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">skip</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">100</span><span class="p">):</span>
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"tasks"</span><span class="p">,</span> <span class="n">Task</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a><span class="w"> </span><span class="sd">"""Get deleted documents"""</span>
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a>
|
||||||
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_many</span><span class="p">(</span>
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a>
|
||||||
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="nb">filter</span><span class="o">=</span><span class="p">{</span><span class="s2">"is_deleted"</span><span class="p">:</span> <span class="kc">True</span><span class="p">},</span>
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="n">skip</span><span class="o">=</span><span class="n">skip</span><span class="p">,</span>
|
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a><span class="n">task_repo</span> <span class="o">=</span> <span class="n">TaskRepository</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="n">limit</span><span class="o">=</span><span class="n">limit</span>
|
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a>
|
||||||
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a> <span class="p">)</span>
|
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a>
|
||||||
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a>
|
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a><span class="nd">@app</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s2">"/tasks/</span><span class="si">{task_id}</span><span class="s2">"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">permanent_delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">soft_delete_task</span><span class="p">(</span><span class="n">task_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a><span class="w"> </span><span class="sd">"""Permanently delete a document"""</span>
|
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="n">task</span> <span class="o">=</span> <span class="k">await</span> <span class="n">task_repo</span><span class="o">.</span><span class="n">soft_delete</span><span class="p">(</span><span class="n">task_id</span><span class="p">,</span> <span class="n">deleted_by</span><span class="o">=</span><span class="n">user_id</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
|
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">task</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a>
|
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"Task not found"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a><span class="k">class</span><span class="w"> </span><span class="nc">TaskRepository</span><span class="p">(</span><span class="n">SoftDeleteRepository</span><span class="p">[</span><span class="n">Task</span><span class="p">]):</span>
|
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a> <span class="k">return</span> <span class="p">{</span><span class="s2">"message"</span><span class="p">:</span> <span class="s2">"Task deleted"</span><span class="p">,</span> <span class="s2">"task"</span><span class="p">:</span> <span class="n">task</span><span class="p">}</span>
|
||||||
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a>
|
||||||
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"tasks"</span><span class="p">,</span> <span class="n">Task</span><span class="p">)</span>
|
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a>
|
||||||
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a>
|
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/tasks/</span><span class="si">{task_id}</span><span class="s2">/restore"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a><span class="c1"># Usage in FastAPI</span>
|
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">restore_task</span><span class="p">(</span><span class="n">task_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a><span class="nd">@app</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s2">"/tasks/</span><span class="si">{task_id}</span><span class="s2">"</span><span class="p">)</span>
|
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a> <span class="n">task</span> <span class="o">=</span> <span class="k">await</span> <span class="n">task_repo</span><span class="o">.</span><span class="n">restore</span><span class="p">(</span><span class="n">task_id</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">soft_delete_task</span><span class="p">(</span><span class="n">task_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">task</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a> <span class="n">task</span> <span class="o">=</span> <span class="k">await</span> <span class="n">task_repo</span><span class="o">.</span><span class="n">soft_delete</span><span class="p">(</span><span class="n">task_id</span><span class="p">,</span> <span class="n">deleted_by</span><span class="o">=</span><span class="n">user_id</span><span class="p">)</span>
|
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"Task not found"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">task</span><span class="p">:</span>
|
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a> <span class="k">return</span> <span class="p">{</span><span class="s2">"message"</span><span class="p">:</span> <span class="s2">"Task restored"</span><span class="p">,</span> <span class="s2">"task"</span><span class="p">:</span> <span class="n">task</span><span class="p">}</span>
|
||||||
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"Task not found"</span><span class="p">)</span>
|
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a>
|
||||||
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a> <span class="k">return</span> <span class="p">{</span><span class="s2">"message"</span><span class="p">:</span> <span class="s2">"Task deleted"</span><span class="p">,</span> <span class="s2">"task"</span><span class="p">:</span> <span class="n">task</span><span class="p">}</span>
|
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a>
|
||||||
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a>
|
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/tasks/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="nb">list</span><span class="p">[</span><span class="n">Task</span><span class="p">])</span>
|
||||||
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/tasks/</span><span class="si">{task_id}</span><span class="s2">/restore"</span><span class="p">)</span>
|
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_active_tasks</span><span class="p">(</span><span class="n">skip</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">restore_task</span><span class="p">(</span><span class="n">task_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">task_repo</span><span class="o">.</span><span class="n">get_active</span><span class="p">(</span><span class="n">skip</span><span class="p">,</span> <span class="n">limit</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a> <span class="n">task</span> <span class="o">=</span> <span class="k">await</span> <span class="n">task_repo</span><span class="o">.</span><span class="n">restore</span><span class="p">(</span><span class="n">task_id</span><span class="p">)</span>
|
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a>
|
||||||
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">task</span><span class="p">:</span>
|
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a>
|
||||||
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"Task not found"</span><span class="p">)</span>
|
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/tasks/deleted"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="nb">list</span><span class="p">[</span><span class="n">Task</span><span class="p">])</span>
|
||||||
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a> <span class="k">return</span> <span class="p">{</span><span class="s2">"message"</span><span class="p">:</span> <span class="s2">"Task restored"</span><span class="p">,</span> <span class="s2">"task"</span><span class="p">:</span> <span class="n">task</span><span class="p">}</span>
|
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_deleted_tasks</span><span class="p">(</span><span class="n">skip</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a>
|
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">task_repo</span><span class="o">.</span><span class="n">get_deleted</span><span class="p">(</span><span class="n">skip</span><span class="p">,</span> <span class="n">limit</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/tasks/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">List</span><span class="p">[</span><span class="n">Task</span><span class="p">])</span>
|
|
||||||
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_active_tasks</span><span class="p">(</span><span class="n">skip</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">):</span>
|
|
||||||
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">task_repo</span><span class="o">.</span><span class="n">get_active</span><span class="p">(</span><span class="n">skip</span><span class="p">,</span> <span class="n">limit</span><span class="p">)</span>
|
|
||||||
<a id="__codelineno-0-79" name="__codelineno-0-79" href="#__codelineno-0-79"></a>
|
|
||||||
<a id="__codelineno-0-80" name="__codelineno-0-80" href="#__codelineno-0-80"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/tasks/deleted"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">List</span><span class="p">[</span><span class="n">Task</span><span class="p">])</span>
|
|
||||||
<a id="__codelineno-0-81" name="__codelineno-0-81" href="#__codelineno-0-81"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_deleted_tasks</span><span class="p">(</span><span class="n">skip</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">):</span>
|
|
||||||
<a id="__codelineno-0-82" name="__codelineno-0-82" href="#__codelineno-0-82"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">task_repo</span><span class="o">.</span><span class="n">get_deleted</span><span class="p">(</span><span class="n">skip</span><span class="p">,</span> <span class="n">limit</span><span class="p">)</span>
|
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
|
<blockquote>
|
||||||
|
<p><strong>Note:</strong> add the <code>lifespan</code> wiring from <a href="../01_basic_crud/">use case 01</a> so <code>task_repo</code> is created after connection.</p>
|
||||||
|
</blockquote>
|
||||||
|
<hr />
|
||||||
|
<h2 id="tips">💡 Tips</h2>
|
||||||
|
<ul>
|
||||||
|
<li><code>BaseRepository[T]</code> is generic — subclasses must parameterize it (see <code>SoftDeleteRepository[T]</code> above). <code>from mongo_ops import BaseRepository; BaseRepository[T]</code> works out of the box.</li>
|
||||||
|
<li>Keep a compound index on <code>{"is_deleted": 1, "status": 1}</code> for active-list queries (see <a href="../13_index_creation/">use case 13</a>).</li>
|
||||||
|
<li>Soft-deleted documents should be <strong>excluded at the repository boundary</strong>, never re-filtered ad hoc in endpoints.</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../01_basic_crud/">01 – Basic CRUD</a> · <a href="../13_index_creation/">13 – Indexes</a> · <a href="../04_pagination/">04 – Pagination</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -645,6 +645,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||||
|
|
||||||
|
|
||||||
@@ -677,6 +679,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__title" for="__toc">
|
<label class="md-nav__title" for="__toc">
|
||||||
<span class="md-nav__icon md-icon"></span>
|
<span class="md-nav__icon md-icon"></span>
|
||||||
Table of contents
|
Table of contents
|
||||||
@@ -684,9 +688,36 @@
|
|||||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#use-case-6-multi-model-service-with-registration" class="md-nav__link">
|
<a href="#whats-new" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Use Case 6: Multi-Model Service with Registration
|
📦 What's New?
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#example" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
🚀 Example
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#tips" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
💡 Tips
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -1004,85 +1035,129 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1>Multi Model</h1>
|
<h1 id="use-case-6-multi-model-service-with-registration">Use Case 6: Multi-Model Service with Registration</h1>
|
||||||
|
<p><strong>Scenario:</strong> A social app manages <code>users</code>, <code>posts</code>, and <code>comments</code>. Each has its own model, repository, and indexes — registered centrally and initialized at startup.</p>
|
||||||
<h3 id="use-case-6-multi-model-service-with-registration">Use Case 6: Multi-Model Service with Registration</h3>
|
<hr />
|
||||||
<p><strong>Scenario:</strong> Complete microservice with multiple related models.</p>
|
<h2 id="whats-new">📦 What's New?</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="p">(</span>
|
<table>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a> <span class="n">MongoConnectionManager</span><span class="p">,</span>
|
<thead>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a> <span class="n">BaseDocument</span><span class="p">,</span>
|
<tr>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a> <span class="n">BaseRepository</span><span class="p">,</span>
|
<th>Component</th>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a> <span class="n">ModelRegistry</span>
|
<th>Description</th>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="p">)</span>
|
</tr>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span>
|
</thead>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
<tbody>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a>
|
<tr>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a><span class="c1"># Define Models</span>
|
<td><code>ModelRegistry.register</code></td>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<td>One call per collection — model + indexes together.</td>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span>
|
</tr>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span>
|
<tr>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">role</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"user"</span>
|
<td><code>ModelRegistry.initialize_all</code></td>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a>
|
<td>Creates every registered index at startup (idempotent).</td>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a><span class="k">class</span><span class="w"> </span><span class="nc">Post</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
</tr>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span>
|
<tr>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="n">content</span><span class="p">:</span> <span class="nb">str</span>
|
<td>Repositories</td>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> <span class="n">author_id</span><span class="p">:</span> <span class="nb">str</span>
|
<td>One repository class per collection, all sharing the same connection.</td>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> <span class="n">likes</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
|
</tr>
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a>
|
</tbody>
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="k">class</span><span class="w"> </span><span class="nc">Comment</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
</table>
|
||||||
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="n">post_id</span><span class="p">:</span> <span class="nb">str</span>
|
<hr />
|
||||||
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span>
|
<h2 id="example">🚀 Example</h2>
|
||||||
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="n">content</span><span class="p">:</span> <span class="nb">str</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a>
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>
|
||||||
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a><span class="c1"># Define Repositories</span>
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span>
|
||||||
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a><span class="k">class</span><span class="w"> </span><span class="nc">UserRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">User</span><span class="p">]):</span>
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">BaseRepository</span><span class="p">,</span> <span class="n">ModelRegistry</span><span class="p">,</span> <span class="n">MongoConnectionManager</span>
|
||||||
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a>
|
||||||
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">User</span><span class="p">)</span>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>
|
||||||
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a><span class="k">class</span><span class="w"> </span><span class="nc">PostRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Post</span><span class="p">]):</span>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"posts"</span><span class="p">,</span> <span class="n">Post</span><span class="p">)</span>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="n">role</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"user"</span>
|
||||||
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a>
|
||||||
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a>
|
||||||
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a><span class="k">class</span><span class="w"> </span><span class="nc">Post</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="n">content</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="n">author_id</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">likes</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
|
||||||
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a>
|
||||||
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a>
|
||||||
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a><span class="k">class</span><span class="w"> </span><span class="nc">Comment</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> <span class="n">post_id</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="n">content</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a>
|
||||||
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a>
|
||||||
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a><span class="k">class</span><span class="w"> </span><span class="nc">UserRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">User</span><span class="p">]):</span>
|
||||||
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">User</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a>
|
||||||
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a>
|
||||||
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a><span class="k">class</span><span class="w"> </span><span class="nc">PostRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Post</span><span class="p">]):</span>
|
||||||
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"posts"</span><span class="p">,</span> <span class="n">Post</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a>
|
||||||
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a>
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a>
|
||||||
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a><span class="k">class</span><span class="w"> </span><span class="nc">CommentRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Comment</span><span class="p">]):</span>
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a><span class="k">class</span><span class="w"> </span><span class="nc">CommentRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Comment</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"comments"</span><span class="p">,</span> <span class="n">Comment</span><span class="p">)</span>
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"comments"</span><span class="p">,</span> <span class="n">Comment</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a>
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a>
|
||||||
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a><span class="c1"># Register all models</span>
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a>
|
||||||
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">User</span><span class="p">,</span> <span class="n">indexes</span><span class="o">=</span><span class="p">[(</span><span class="s2">"email"</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="s2">"username"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)])</span>
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s2">"posts"</span><span class="p">,</span> <span class="n">Post</span><span class="p">,</span> <span class="n">indexes</span><span class="o">=</span><span class="p">[(</span><span class="s2">"author_id"</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="s2">"created_at"</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)])</span>
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a><span class="c1"># Central registration + indexes</span>
|
||||||
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s2">"comments"</span><span class="p">,</span> <span class="n">Comment</span><span class="p">,</span> <span class="n">indexes</span><span class="o">=</span><span class="p">[(</span><span class="s2">"post_id"</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="s2">"user_id"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)])</span>
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a>
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">User</span><span class="p">,</span> <span class="n">indexes</span><span class="o">=</span><span class="p">[(</span><span class="s2">"email"</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="s2">"username"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)])</span>
|
||||||
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a><span class="c1"># Setup FastAPI with lifespan</span>
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s2">"posts"</span><span class="p">,</span> <span class="n">Post</span><span class="p">,</span> <span class="n">indexes</span><span class="o">=</span><span class="p">[(</span><span class="s2">"author_id"</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="s2">"created_at"</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)])</span>
|
||||||
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a><span class="nd">@asynccontextmanager</span>
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s2">"comments"</span><span class="p">,</span> <span class="n">Comment</span><span class="p">,</span> <span class="n">indexes</span><span class="o">=</span><span class="p">[(</span><span class="s2">"post_id"</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="s2">"user_id"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)])</span>
|
||||||
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">app</span><span class="p">:</span> <span class="n">FastAPI</span><span class="p">):</span>
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a>
|
||||||
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="k">await</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span>
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a>
|
||||||
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span>
|
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="n">db_name</span><span class="o">=</span><span class="s2">"social_app"</span>
|
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a><span class="c1"># Lifecycle</span>
|
||||||
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a> <span class="p">)</span>
|
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a><span class="c1"># ---------------------------</span>
|
||||||
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a><span class="nd">@asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a> <span class="k">yield</span>
|
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">_app</span><span class="p">:</span> <span class="n">FastAPI</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="k">await</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">disconnect</span><span class="p">()</span>
|
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a>
|
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span> <span class="n">db_name</span><span class="o">=</span><span class="s2">"social_app"</span>
|
||||||
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">(</span><span class="n">lifespan</span><span class="o">=</span><span class="n">lifespan</span><span class="p">)</span>
|
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a> <span class="p">):</span>
|
||||||
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a>
|
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a><span class="c1"># Initialize repositories</span>
|
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> <span class="k">yield</span>
|
||||||
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a><span class="n">user_repo</span> <span class="o">=</span> <span class="n">UserRepository</span><span class="p">()</span>
|
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a>
|
||||||
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a><span class="n">post_repo</span> <span class="o">=</span> <span class="n">PostRepository</span><span class="p">()</span>
|
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a>
|
||||||
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a><span class="n">comment_repo</span> <span class="o">=</span> <span class="n">CommentRepository</span><span class="p">()</span>
|
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">(</span><span class="n">lifespan</span><span class="o">=</span><span class="n">lifespan</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a>
|
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a>
|
||||||
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a><span class="c1"># Endpoints</span>
|
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a>
|
||||||
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/users/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">User</span><span class="p">)</span>
|
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a><span class="c1"># Created after connect() — inside the lifespan body is fine, or use a dependency.</span>
|
||||||
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_user</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span><span class="p">):</span>
|
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a><span class="n">user_repo</span> <span class="o">=</span> <span class="n">UserRepository</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">user_repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a><span class="n">post_repo</span> <span class="o">=</span> <span class="n">PostRepository</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a>
|
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a><span class="n">comment_repo</span> <span class="o">=</span> <span class="n">CommentRepository</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/posts/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">Post</span><span class="p">)</span>
|
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a>
|
||||||
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_post</span><span class="p">(</span><span class="n">post</span><span class="p">:</span> <span class="n">Post</span><span class="p">):</span>
|
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a>
|
||||||
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">post_repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">post</span><span class="p">)</span>
|
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/users/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">User</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a>
|
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_user</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/comments/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">Comment</span><span class="p">)</span>
|
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">user_repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_comment</span><span class="p">(</span><span class="n">comment</span><span class="p">:</span> <span class="n">Comment</span><span class="p">):</span>
|
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a>
|
||||||
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">comment_repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">comment</span><span class="p">)</span>
|
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a>
|
||||||
|
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/posts/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">Post</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_post</span><span class="p">(</span><span class="n">post</span><span class="p">:</span> <span class="n">Post</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">post_repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">post</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a>
|
||||||
|
<a id="__codelineno-0-79" name="__codelineno-0-79" href="#__codelineno-0-79"></a>
|
||||||
|
<a id="__codelineno-0-80" name="__codelineno-0-80" href="#__codelineno-0-80"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/comments/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">Comment</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-81" name="__codelineno-0-81" href="#__codelineno-0-81"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_comment</span><span class="p">(</span><span class="n">comment</span><span class="p">:</span> <span class="n">Comment</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-82" name="__codelineno-0-82" href="#__codelineno-0-82"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">comment_repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">comment</span><span class="p">)</span>
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
|
<hr />
|
||||||
|
<h2 id="tips">💡 Tips</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Place <code>user_repo = ...</code> inside the lifespan/after connect.</strong> Module-level instantiation before <code>connect()</code> raises <code>RuntimeError("Database not connected...")</code>.</li>
|
||||||
|
<li>Model relationships here are plain <code>ObjectId</code> strings stored on the child docs. To resolve them on read, see <a href="../08_population/">use case 08 – Population</a>.</li>
|
||||||
|
<li>Registering indexes on <code>created_at</code>/<code>author_id</code>/<code>post_id</code> keeps the common queries indexed (see <a href="../13_index_creation/">use case 13</a>).</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../01_basic_crud/">01 – Basic CRUD</a> · <a href="../08_population/">08 – Population</a> · <a href="../13_index_creation/">13 – Indexes</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
<div data-md-component="skip">
|
<div data-md-component="skip">
|
||||||
|
|
||||||
|
|
||||||
<a href="#use-case-7-caching-for-highperformance-reads" class="md-skip">
|
<a href="#use-case-7-caching-for-high-performance-reads" class="md-skip">
|
||||||
Skip to content
|
Skip to content
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -711,54 +711,45 @@
|
|||||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#1-whats-new" class="md-nav__link">
|
<a href="#whats-new" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
1️⃣ What’s New?
|
📦 What's New?
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#2-quick-start" class="md-nav__link">
|
<a href="#example" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
2️⃣ Quick Start
|
🚀 Example
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#3-using-the-repository" class="md-nav__link">
|
<a href="#redis-backend" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
3️⃣ Using the Repository
|
🔄 Redis Backend
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#4-redis-backend-optional" class="md-nav__link">
|
<a href="#tips" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
4️⃣ Redis Backend (optional)
|
💡 Tips
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#5-when-to-use-caching" class="md-nav__link">
|
<a href="#related" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
5️⃣ When to Use Caching
|
Related
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="md-nav__item">
|
|
||||||
<a href="#6-related-docs" class="md-nav__link">
|
|
||||||
<span class="md-ellipsis">
|
|
||||||
6️⃣ Related Docs
|
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -1053,104 +1044,139 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1 id="use-case-7-caching-for-highperformance-reads">Use Case 7: Caching for High‑Performance Reads</h1>
|
<h1 id="use-case-7-caching-for-high-performance-reads">Use Case 7: Caching for High-Performance Reads</h1>
|
||||||
<p><strong>Scenario:</strong> A read‑heavy API (e.g., product catalog) benefits from an in‑memory cache or Redis cache to reduce latency and DB load.</p>
|
<p><strong>Scenario:</strong> A read-heavy API (product catalog) reduces DB load by caching documents by <code>_id</code> — in-memory locally, or shared via Redis.</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="1-whats-new">1️⃣ What’s New?</h2>
|
<h2 id="whats-new">📦 What's New?</h2>
|
||||||
<ul>
|
<table>
|
||||||
<li><strong>Cache back‑ends</strong>: <code>InMemoryCacheBackend</code> (TTL + LRU) and <code>RedisCacheBackend</code> (JSON‑serialised values, pub/sub invalidation).</li>
|
<thead>
|
||||||
<li><strong>Cache configuration</strong> via <code>CacheConfig</code> (<code>enabled</code>, <code>backend</code>, <code>default_ttl</code>, …).</li>
|
<tr>
|
||||||
<li><strong><code>CachedBaseRepository</code></strong> extends <code>BaseRepository</code> and adds:</li>
|
<th>Component</th>
|
||||||
<li>Transparent ID‑based caching on <code>get_by_id</code>.</li>
|
<th>Description</th>
|
||||||
<li>Automatic cache population on <code>create</code>.</li>
|
</tr>
|
||||||
<li>Cache invalidation on <code>update</code>/<code>delete</code>.</li>
|
</thead>
|
||||||
<li>Utility methods <code>warm_cache(ids)</code> and <code>invalidate_cache(id)</code>.</li>
|
<tbody>
|
||||||
</ul>
|
<tr>
|
||||||
|
<td><code>InMemoryCacheBackend</code></td>
|
||||||
|
<td>TTL + LRU cache with a background cleanup task.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>RedisCacheBackend</code></td>
|
||||||
|
<td>Distributed cache on <code>redis.asyncio</code> with pub/sub invalidation.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>CachedBaseRepository[T]</code></td>
|
||||||
|
<td>Extends <code>BaseRepository</code> — cache-first <code>get_by_id</code>, cache on <code>create</code>, invalidate on <code>update</code>/<code>delete</code>, <code>warm_cache(ids)</code>, <code>invalidate_cache(id)</code>.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Backend lifecycle</td>
|
||||||
|
<td>The <strong>same</strong> backend instance must be both passed to the repository AND registered via <code>ModelRegistry.set_cache_backend</code> so <code>initialize_cache()</code> starts its task.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="2-quick-start">2️⃣ Quick Start</h2>
|
<h2 id="example">🚀 Example</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="p">(</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a> <span class="n">BaseDocument</span><span class="p">,</span>
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a> <span class="n">CachedBaseRepository</span><span class="p">,</span>
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span><span class="p">,</span> <span class="n">HTTPException</span>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a> <span class="n">MongoConnectionManager</span><span class="p">,</span>
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">ModelRegistry</span><span class="p">,</span> <span class="n">MongoConnectionManager</span>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a> <span class="n">ModelRegistry</span><span class="p">,</span>
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.cache</span><span class="w"> </span><span class="kn">import</span> <span class="n">CacheConfig</span><span class="p">,</span> <span class="n">CachedBaseRepository</span><span class="p">,</span> <span class="n">InMemoryCacheBackend</span>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a> <span class="n">CacheConfig</span><span class="p">,</span>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="p">)</span>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.cache</span><span class="w"> </span><span class="kn">import</span> <span class="n">InMemoryCacheBackend</span>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="k">class</span><span class="w"> </span><span class="nc">Product</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="n">price</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="mf">0.0</span>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a><span class="c1"># Define a model</span>
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a><span class="k">class</span><span class="w"> </span><span class="nc">Product</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a><span class="c1"># One shared backend — used by both the repository and the registry lifecycle.</span>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">price</span><span class="p">:</span> <span class="nb">float</span>
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a><span class="n">cache</span> <span class="o">=</span> <span class="n">InMemoryCacheBackend</span><span class="p">(</span><span class="n">max_entries</span><span class="o">=</span><span class="mi">10_000</span><span class="p">,</span> <span class="n">default_ttl</span><span class="o">=</span><span class="mi">300</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a>
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">set_cache_backend</span><span class="p">(</span><span class="n">cache</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a><span class="c1"># Initialise cache backend (in‑memory example)</span>
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a><span class="n">cache</span> <span class="o">=</span> <span class="n">InMemoryCacheBackend</span><span class="p">(</span><span class="n">max_entries</span><span class="o">=</span><span class="mi">10_000</span><span class="p">,</span> <span class="n">default_ttl</span><span class="o">=</span><span class="mi">300</span><span class="p">)</span>
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a><span class="c1"># Register the cache so the registry can initialise it later</span>
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a><span class="k">class</span><span class="w"> </span><span class="nc">ProductRepo</span><span class="p">(</span><span class="n">CachedBaseRepository</span><span class="p">[</span><span class="n">Product</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">set_cache_backend</span><span class="p">(</span><span class="n">cache</span><span class="p">)</span>
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a>
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="c1"># Repository with caching</span>
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"products"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="k">class</span><span class="w"> </span><span class="nc">ProductRepo</span><span class="p">(</span><span class="n">CachedBaseRepository</span><span class="p">[</span><span class="n">Product</span><span class="p">]):</span>
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="n">model</span><span class="o">=</span><span class="n">Product</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="n">cache_backend</span><span class="o">=</span><span class="n">cache</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span>
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="n">config</span><span class="o">=</span><span class="n">CacheConfig</span><span class="p">(</span><span class="n">enabled</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">backend</span><span class="o">=</span><span class="s2">"memory"</span><span class="p">),</span>
|
||||||
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"products"</span><span class="p">,</span>
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="p">)</span>
|
||||||
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> <span class="n">model</span><span class="o">=</span><span class="n">Product</span><span class="p">,</span>
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a>
|
||||||
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a> <span class="n">cache_backend</span><span class="o">=</span><span class="n">cache</span><span class="p">,</span>
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a>
|
||||||
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a> <span class="n">config</span><span class="o">=</span><span class="n">CacheConfig</span><span class="p">(</span><span class="n">enabled</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">backend</span><span class="o">=</span><span class="s2">"memory"</span><span class="p">)</span>
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a><span class="nd">@asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a> <span class="p">)</span>
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">_app</span><span class="p">:</span> <span class="n">FastAPI</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a>
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a><span class="c1"># FastAPI lifespan – initialise DB and cache</span>
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span> <span class="n">db_name</span><span class="o">=</span><span class="s2">"shop"</span>
|
||||||
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="p">):</span>
|
||||||
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span> <span class="n">db_name</span><span class="o">=</span><span class="s2">"shop"</span>
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_cache</span><span class="p">()</span> <span class="c1"># starts the TTL cleanup task</span>
|
||||||
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="p">):</span>
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="k">yield</span>
|
||||||
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">shutdown_cache</span><span class="p">()</span> <span class="c1"># cancels it on exit</span>
|
||||||
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_cache</span><span class="p">()</span> <span class="c1"># Starts background cleanup, etc.</span>
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a>
|
||||||
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="k">yield</span>
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a>
|
||||||
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">(</span><span class="n">lifespan</span><span class="o">=</span><span class="n">lifespan</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a>
|
||||||
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a>
|
||||||
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/products/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">Product</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_product</span><span class="p">(</span><span class="n">product</span><span class="p">:</span> <span class="n">Product</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">ProductRepo</span><span class="p">()</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">product</span><span class="p">)</span> <span class="c1"># created after connect()</span>
|
||||||
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a>
|
||||||
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a>
|
||||||
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/products/</span><span class="si">{product_id}</span><span class="s2">"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">Product</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_product</span><span class="p">(</span><span class="n">product_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="n">product</span> <span class="o">=</span> <span class="k">await</span> <span class="n">ProductRepo</span><span class="p">()</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="n">product_id</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">product</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"Product not found"</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a> <span class="k">return</span> <span class="n">product</span>
|
||||||
|
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a>
|
||||||
|
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a>
|
||||||
|
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a><span class="nd">@app</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="s2">"/products/</span><span class="si">{product_id}</span><span class="s2">"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">Product</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_product</span><span class="p">(</span><span class="n">product_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span> <span class="n">price</span><span class="p">:</span> <span class="nb">float</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a> <span class="n">data</span> <span class="o">=</span> <span class="p">{}</span>
|
||||||
|
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> <span class="k">if</span> <span class="n">name</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> <span class="n">data</span><span class="p">[</span><span class="s2">"name"</span><span class="p">]</span> <span class="o">=</span> <span class="n">name</span>
|
||||||
|
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a> <span class="k">if</span> <span class="n">price</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a> <span class="n">data</span><span class="p">[</span><span class="s2">"price"</span><span class="p">]</span> <span class="o">=</span> <span class="n">price</span>
|
||||||
|
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">ProductRepo</span><span class="p">()</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">product_id</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a>
|
||||||
|
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a>
|
||||||
|
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a><span class="c1"># Outside request handlers:</span>
|
||||||
|
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a><span class="c1"># await ProductRepo().warm_cache([object_id_1, object_id_2]) # pre-load n ids -> int</span>
|
||||||
|
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a><span class="c1"># await ProductRepo().invalidate_cache(object_id_3) # manual eviction</span>
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="3-using-the-repository">3️⃣ Using the Repository</h2>
|
<h2 id="redis-backend">🔄 Redis Backend</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="n">repo</span> <span class="o">=</span> <span class="n">ProductRepo</span><span class="p">()</span>
|
<p>Swap the backend — the repository code stays identical:</p>
|
||||||
<a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a><span class="c1"># Create – automatically caches the new document</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.cache</span><span class="w"> </span><span class="kn">import</span> <span class="n">RedisCacheBackend</span>
|
||||||
<a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a><span class="n">product</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">Product</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">"Widget"</span><span class="p">,</span> <span class="n">price</span><span class="o">=</span><span class="mf">9.99</span><span class="p">))</span>
|
<a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">redis.asyncio</span><span class="w"> </span><span class="kn">import</span> <span class="n">Redis</span>
|
||||||
<a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a>
|
<a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a>
|
||||||
<a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a><span class="c1"># Normal read – will hit the cache after the first DB fetch</span>
|
<a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a><span class="n">redis_client</span> <span class="o">=</span> <span class="n">Redis</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="s2">"localhost"</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">6379</span><span class="p">)</span>
|
||||||
<a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a><span class="n">fetched</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="n">product</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
|
<a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a><span class="n">redis_backend</span> <span class="o">=</span> <span class="n">RedisCacheBackend</span><span class="p">(</span><span class="n">redis_client</span><span class="p">,</span> <span class="n">key_prefix</span><span class="o">=</span><span class="s2">"prod:"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a>
|
<a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a>
|
||||||
<a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></a><span class="c1"># Update – cache entry is refreshed</span>
|
<a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">set_cache_backend</span><span class="p">(</span><span class="n">redis_backend</span><span class="p">)</span> <span class="c1"># for the lifecycle in lifespan()</span>
|
||||||
<a id="__codelineno-1-9" name="__codelineno-1-9" href="#__codelineno-1-9"></a><span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">product</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="p">{</span><span class="s2">"price"</span><span class="p">:</span> <span class="mf">8.99</span><span class="p">})</span>
|
<a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></a>
|
||||||
<a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a>
|
<a id="__codelineno-1-9" name="__codelineno-1-9" href="#__codelineno-1-9"></a><span class="k">class</span><span class="w"> </span><span class="nc">ProductRepo</span><span class="p">(</span><span class="n">CachedBaseRepository</span><span class="p">[</span><span class="n">Product</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-1-11" name="__codelineno-1-11" href="#__codelineno-1-11"></a><span class="c1"># Delete – cache entry removed</span>
|
<a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-1-12" name="__codelineno-1-12" href="#__codelineno-1-12"></a><span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">product</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
|
<a id="__codelineno-1-11" name="__codelineno-1-11" href="#__codelineno-1-11"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span>
|
||||||
<a id="__codelineno-1-13" name="__codelineno-1-13" href="#__codelineno-1-13"></a>
|
<a id="__codelineno-1-12" name="__codelineno-1-12" href="#__codelineno-1-12"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"products"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-1-14" name="__codelineno-1-14" href="#__codelineno-1-14"></a><span class="c1"># Warm a set of IDs in advance (e.g., during a bulk load)</span>
|
<a id="__codelineno-1-13" name="__codelineno-1-13" href="#__codelineno-1-13"></a> <span class="n">model</span><span class="o">=</span><span class="n">Product</span><span class="p">,</span>
|
||||||
<a id="__codelineno-1-15" name="__codelineno-1-15" href="#__codelineno-1-15"></a><span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">warm_cache</span><span class="p">([</span><span class="n">ObjectId</span><span class="p">(</span><span class="s2">"..."</span><span class="p">),</span> <span class="n">ObjectId</span><span class="p">(</span><span class="s2">"..."</span><span class="p">)])</span>
|
<a id="__codelineno-1-14" name="__codelineno-1-14" href="#__codelineno-1-14"></a> <span class="n">cache_backend</span><span class="o">=</span><span class="n">redis_backend</span><span class="p">,</span>
|
||||||
|
<a id="__codelineno-1-15" name="__codelineno-1-15" href="#__codelineno-1-15"></a> <span class="n">config</span><span class="o">=</span><span class="n">CacheConfig</span><span class="p">(</span><span class="n">enabled</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">backend</span><span class="o">=</span><span class="s2">"redis"</span><span class="p">),</span>
|
||||||
|
<a id="__codelineno-1-16" name="__codelineno-1-16" href="#__codelineno-1-16"></a> <span class="p">)</span>
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="4-redis-backend-optional">4️⃣ Redis Backend (optional)</h2>
|
<h2 id="tips">💡 Tips</h2>
|
||||||
<p>If you prefer a distributed cache, swap the backend:</p>
|
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.cache</span><span class="w"> </span><span class="kn">import</span> <span class="n">RedisCacheBackend</span>
|
|
||||||
<a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">redis.asyncio</span><span class="w"> </span><span class="kn">import</span> <span class="n">Redis</span>
|
|
||||||
<a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a>
|
|
||||||
<a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a><span class="n">redis_client</span> <span class="o">=</span> <span class="n">Redis</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="s2">"localhost"</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">6379</span><span class="p">)</span>
|
|
||||||
<a id="__codelineno-2-5" name="__codelineno-2-5" href="#__codelineno-2-5"></a><span class="n">redis_backend</span> <span class="o">=</span> <span class="n">RedisCacheBackend</span><span class="p">(</span><span class="n">redis_client</span><span class="p">,</span> <span class="n">key_prefix</span><span class="o">=</span><span class="s2">"prod:"</span><span class="p">)</span>
|
|
||||||
<a id="__codelineno-2-6" name="__codelineno-2-6" href="#__codelineno-2-6"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">set_cache_backend</span><span class="p">(</span><span class="n">redis_backend</span><span class="p">)</span>
|
|
||||||
</code></pre></div>
|
|
||||||
<p>The repository code stays the same – just pass the <code>redis_backend</code> instance to <code>CachedBaseRepository</code>.</p>
|
|
||||||
<hr />
|
|
||||||
<h2 id="5-when-to-use-caching">5️⃣ When to Use Caching</h2>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Frequently accessed documents (e.g., product details, configuration settings).</li>
|
<li>Cache keys are <code>"{key_prefix}{id}"</code> (default prefix <code>"products:"</code>). <code>clear_pattern("products:*")</code> wipes a whole collection's entries.</li>
|
||||||
<li>Low‑write‑to‑read ratios where cache invalidation cost is acceptable.</li>
|
<li>Values are JSON-encoded (<code>json.dumps(obj, default=str)</code>) — nested models inside a cached document are stored as dicts, not objects.</li>
|
||||||
<li>Distributed deployments where a shared Redis cache syncs invalidations via pub/sub.</li>
|
<li>Enable/disable per repository with <code>CacheConfig(enabled=False)</code>; a disabled repo bypasses the cache entirely.</li>
|
||||||
|
<li>Distinguish the two <code>initialize*</code> calls: <code>initialize_cache()</code> starts the backend task; <code>initialize_all()</code> creates indexes. Both belong in the lifespan, <strong>after</strong> <code>connect()</code>.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="6-related-docs">6️⃣ Related Docs</h2>
|
<h2 id="related">Related</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Core Components</strong> – see <code>docs/02_components.md</code> for the <code>CacheBackend</code> abstraction.</li>
|
<li><a href="../11_cache_lifecycle/">11 – Cache lifecycle</a> · <a href="../10_cache_and_population/">10 – Cache + population</a> · <a href="../../02_components/">Components: cache</a></li>
|
||||||
<li><strong>Best Practices</strong> – remember to call <code>ModelRegistry.initialize_cache()</code> after DB connection.</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
|
||||||
<p><em>Feel free to adapt the TTL, max entries, and backend to your workload.</em></p>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -736,16 +736,34 @@
|
|||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#whats-new" class="md-nav__link">
|
<a href="#whats-new" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
📦 What’s New?
|
📦 What's New?
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#quick-example" class="md-nav__link">
|
<a href="#example" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
🚀 Quick Example
|
🚀 Example
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#tips" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
💡 Tips
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -1018,10 +1036,9 @@
|
|||||||
|
|
||||||
|
|
||||||
<h1 id="use-case-8-document-population">Use Case 8: Document Population</h1>
|
<h1 id="use-case-8-document-population">Use Case 8: Document Population</h1>
|
||||||
<p><strong>Scenario:</strong><br />
|
<p><strong>Scenario:</strong> An API returns a <code>User</code> with its related <code>Profile</code> embedded in one JSON payload — no second round-trip from the client, no joins.</p>
|
||||||
An API needs to return a single, denormalised JSON payload that includes related documents (e.g., a <code>User</code> with an embedded <code>Profile</code>, or an <code>Order</code> with its <code>LineItem</code>s) without the caller having to issue multiple requests.</p>
|
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="whats-new">📦 What’s New?</h2>
|
<h2 id="whats-new">📦 What's New?</h2>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -1031,99 +1048,109 @@ An API needs to return a single, denormalised JSON payload that includes related
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong><code>PopulateRule</code></strong></td>
|
<td><code>PopulateRule</code></td>
|
||||||
<td>Declarative rule that tells the engine how to populate a field.<br>Key attributes:<br>• <code>field_name</code> – Target attribute on the source model.<br>• <code>collection_name</code> – Collection that stores the referenced documents.<br>• <code>ref_field</code> – Name of the reference field (normally an <code>ObjectId</code>).<br>• Optional <code>nested_rules</code> for deep population, <code>max_depth</code> to bound recursion, and <code>filter</code> / <code>projection</code> for fine‑grained queries.</td>
|
<td>Declares <em>which field</em> to resolve. <code>field_name</code> holds the <code>ObjectId</code> (or <code>list[ObjectId]</code>) <strong>and is the same field the resolved document replaces in place</strong>. <code>collection_name</code> says where the referenced documents live.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong><code>PopulationEngine</code></strong></td>
|
<td><code>PopulationEngine</code></td>
|
||||||
<td>Core engine that resolves a list of <code>PopulateRule</code>s recursively. It:<br>• Detects circular references and raises <code>CircularReferenceError</code>.<br>• Honors per‑rule <code>max_depth</code> and a global <code>global_max_depth</code> (default 10).<br>• Supports MongoDB filters and projections for each populated relation.</td>
|
<td>Holds repositories by collection name and resolves rules recursively, detecting cycles (<code>CircularReferenceError</code>).</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong><code>PopulatingRepository</code></strong></td>
|
<td><code>PopulatingRepository[T]</code></td>
|
||||||
<td>Extends <code>BaseRepository</code> with automatic population support. Stores a reference to a <code>PopulationEngine</code> and a list of <code>PopulateRule</code>s, exposing two helpers:<br>• <code>_populate(document)</code> – Returns the same document with the requested relations populated.<br>• <code>_depopulate(document)</code> – Strips populated fields (useful before serialisation).</td>
|
<td><code>get_by_id</code>/<code>get_many</code> populate on read; <code>create</code>/<code>update</code> depopulate on write; <code>patch</code> rejects FK fields.</td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><strong>Cache‑aware population</strong> (optional) – The engine works with any <code>CacheBackend</code>. When used together with <code>CachedBaseRepository</code>, populated documents benefit from caching in the same way as regular CRUD results.</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<blockquote>
|
||||||
|
<p>⚠️ <code>PopulateRule</code> does <strong>not</strong> have a separate "ref field" vs "target field" — the ref field is the populated field. <code>filter</code>/<code>projection</code> on <code>PopulateRule</code> are declared but not yet applied by the engine.</p>
|
||||||
|
</blockquote>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="quick-example">🚀 Quick Example</h2>
|
<h2 id="example">🚀 Example</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">PopulatingRepository</span><span class="p">,</span> <span class="n">ModelRegistry</span>
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.populate</span><span class="w"> </span><span class="kn">import</span> <span class="n">PopulateRule</span><span class="p">,</span> <span class="n">PopulationEngine</span>
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span><span class="p">,</span> <span class="n">HTTPException</span>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.cache</span><span class="w"> </span><span class="kn">import</span> <span class="n">InMemoryCacheBackend</span>
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">MongoConnectionManager</span><span class="p">,</span> <span class="n">ModelRegistry</span><span class="p">,</span> <span class="n">PopulatingRepository</span>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a>
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.populate</span><span class="w"> </span><span class="kn">import</span> <span class="n">PopulateRule</span><span class="p">,</span> <span class="n">PopulationEngine</span>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="c1"># 1️⃣ Define the data models</span>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="c1"># 1. Models — `profile` holds an ObjectId in the DB and becomes a Profile on read.</span>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="k">class</span><span class="w"> </span><span class="nc">Profile</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="k">class</span><span class="w"> </span><span class="nc">Profile</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="n">avatar_url</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="n">avatar_url</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> <span class="n">bio</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> <span class="n">bio</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a>
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="n">profile_id</span><span class="p">:</span> <span class="n">ObjectId</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Reference to Profile</span>
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">profile</span><span class="p">:</span> <span class="n">Profile</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Populated field (filled by engine)</span>
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">profile</span><span class="p">:</span> <span class="n">Profile</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># ObjectId in MongoDB, Profile in memory</span>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a>
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a><span class="c1"># 2️⃣ (Optional) Set up a cache; helpful for deep graphs</span>
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a><span class="c1"># 2. Engine + rule — repositories are registered later (after connect()).</span>
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="n">engine</span> <span class="o">=</span> <span class="n">PopulationEngine</span><span class="p">({})</span>
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="n">cache</span> <span class="o">=</span> <span class="n">InMemoryCacheBackend</span><span class="p">(</span><span class="n">max_entries</span><span class="o">=</span><span class="mi">10_000</span><span class="p">,</span> <span class="n">default_ttl</span><span class="o">=</span><span class="mi">300</span><span class="p">)</span>
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="n">profile_rule</span> <span class="o">=</span> <span class="n">PopulateRule</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">set_cache_backend</span><span class="p">(</span><span class="n">cache</span><span class="p">)</span>
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="n">field_name</span><span class="o">=</span><span class="s2">"profile"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a>
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"profiles"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a><span class="p">)</span>
|
||||||
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a><span class="c1"># 3️⃣ Create a PopulationEngine – repositories will be registered later</span>
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a>
|
||||||
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a>
|
||||||
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a><span class="n">engine</span> <span class="o">=</span> <span class="n">PopulationEngine</span><span class="p">({})</span>
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a><span class="c1"># 3. The repository the app uses.</span>
|
||||||
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a>
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a><span class="k">class</span><span class="w"> </span><span class="nc">UserRepository</span><span class="p">(</span><span class="n">PopulatingRepository</span><span class="p">[</span><span class="n">User</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a><span class="c1"># 4️⃣ Declare how the `profile` field should be populated</span>
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"users"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a><span class="n">profile_rule</span> <span class="o">=</span> <span class="n">PopulateRule</span><span class="p">(</span>
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="n">model</span><span class="o">=</span><span class="n">User</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="n">field_name</span><span class="o">=</span><span class="s2">"profile"</span><span class="p">,</span>
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="n">population_engine</span><span class="o">=</span><span class="n">engine</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"profiles"</span><span class="p">,</span>
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="n">populate_rules</span><span class="o">=</span><span class="p">[</span><span class="n">profile_rule</span><span class="p">],</span>
|
||||||
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="n">ref_field</span><span class="o">=</span><span class="s2">"profile_id"</span><span class="p">,</span>
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="p">)</span>
|
||||||
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="c1"># No further nesting in this simple example</span>
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a>
|
||||||
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a><span class="p">)</span>
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a>
|
||||||
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a>
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a><span class="nd">@asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">_app</span><span class="p">:</span> <span class="n">FastAPI</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a><span class="c1"># 5️⃣ Repository that uses the engine + rule</span>
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span> <span class="n">db_name</span><span class="o">=</span><span class="s2">"mydb"</span>
|
||||||
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a><span class="k">class</span><span class="w"> </span><span class="nc">UserRepository</span><span class="p">(</span><span class="n">PopulatingRepository</span><span class="p">[</span><span class="n">User</span><span class="p">]):</span>
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="p">):</span>
|
||||||
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="c1"># Tells the engine which repository resolves "profiles" refs.</span>
|
||||||
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span>
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="n">engine</span><span class="o">.</span><span class="n">register_repo</span><span class="p">(</span><span class="s2">"profiles"</span><span class="p">,</span> <span class="n">PopulatingRepository</span><span class="p">[</span><span class="n">Profile</span><span class="p">](</span><span class="s2">"profiles"</span><span class="p">,</span> <span class="n">Profile</span><span class="p">))</span>
|
||||||
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"users"</span><span class="p">,</span>
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="n">model</span><span class="o">=</span><span class="n">User</span><span class="p">,</span>
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="k">yield</span>
|
||||||
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="n">population_engine</span><span class="o">=</span><span class="n">engine</span><span class="p">,</span>
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a>
|
||||||
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="n">populate_rules</span><span class="o">=</span><span class="p">[</span><span class="n">profile_rule</span><span class="p">],</span>
|
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a>
|
||||||
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="p">)</span>
|
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">(</span><span class="n">lifespan</span><span class="o">=</span><span class="n">lifespan</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a>
|
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a>
|
||||||
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a>
|
||||||
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a><span class="c1"># 6️⃣ Register the repository with the engine so it can resolve refs</span>
|
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a><span class="c1"># 4. Writing — pass the model; the repository depopulates to an ObjectId.</span>
|
||||||
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/users/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">User</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a><span class="n">engine</span><span class="o">.</span><span class="n">register_repo</span><span class="p">(</span><span class="s2">"profiles"</span><span class="p">,</span> <span class="n">PopulatingRepository</span><span class="p">[</span><span class="n">Profile</span><span class="p">](</span><span class="s2">"profiles"</span><span class="p">,</span> <span class="n">Profile</span><span class="p">))</span>
|
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_user</span><span class="p">():</span>
|
||||||
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a><span class="n">engine</span><span class="o">.</span><span class="n">register_repo</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">UserRepository</span><span class="p">())</span>
|
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a> <span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a>
|
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a> <span class="n">username</span><span class="o">=</span><span class="s2">"alice"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> <span class="n">email</span><span class="o">=</span><span class="s2">"alice@example.com"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a><span class="c1"># 7️⃣ FastAPI lifespan – initialise DB, indexes, and the cache</span>
|
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> <span class="n">profile</span><span class="o">=</span><span class="n">Profile</span><span class="p">(</span><span class="n">avatar_url</span><span class="o">=</span><span class="s2">"alice.png"</span><span class="p">,</span> <span class="n">bio</span><span class="o">=</span><span class="s2">"hi"</span><span class="p">),</span>
|
||||||
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a> <span class="p">)</span>
|
||||||
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">MongoConnectionManager</span>
|
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">UserRepository</span><span class="p">()</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a> <span class="c1"># ^ stored as {"profile": <ObjectId>}, returned with profile populated</span>
|
||||||
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a>
|
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a>
|
||||||
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a><span class="nd">@asynccontextmanager</span>
|
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a>
|
||||||
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
|
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a><span class="c1"># 5. Reading — get_by_id resolves the reference on the way back.</span>
|
||||||
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/users/</span><span class="si">{user_id}</span><span class="s2">"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">User</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span>
|
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_user</span><span class="p">(</span><span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a> <span class="n">db_name</span><span class="o">=</span><span class="s2">"mydb"</span>
|
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a> <span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserRepository</span><span class="p">()</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a> <span class="p">):</span>
|
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"User not found"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a> <span class="c1"># If you set up a cache backend (see step 2), initialize it here</span>
|
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a> <span class="k">return</span> <span class="n">user</span> <span class="c1"># user.profile is a Profile instance</span>
|
||||||
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_cache</span><span class="p">()</span>
|
|
||||||
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a> <span class="k">yield</span>
|
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
|
<hr />
|
||||||
|
<h2 id="tips">💡 Tips</h2>
|
||||||
|
<ul>
|
||||||
|
<li><code>Patch</code>ing an FK field raises <code>ValueError</code> — switch FK changes to <code>update(user_id, model)</code> instead. See <a href="../05_soft_deletes/">use case 05</a> for the reasoning with soft deletes.</li>
|
||||||
|
<li>Missing references resolve to <code>None</code>, not an error.</li>
|
||||||
|
<li>A field holding an embedded dict (instead of an <code>ObjectId</code>) raises a <code>ValueError</code> ("run repair script") — migrate embedded docs to a separate collection first.</li>
|
||||||
|
<li>If <code>create</code>/<code>update</code> receives a field that is <em>already</em> an <code>ObjectId</code> under a populate rule, <code>_depopulate</code> raises <code>ValueError("...was populate skipped?")</code> — populate-then-depopulate pairs must be balanced.</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../09_advanced_population/">09 – Advanced population</a> · <a href="../10_cache_and_population/">10 – Cache + population</a> · <a href="../14_testing_guide/">14 – Testing guide</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
<div data-md-component="skip">
|
<div data-md-component="skip">
|
||||||
|
|
||||||
|
|
||||||
<a href="#use-case-9-nested-document-population-circularref-handling" class="md-skip">
|
<a href="#use-case-9-nested-document-population-circular-ref-handling" class="md-skip">
|
||||||
Skip to content
|
Skip to content
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -759,7 +759,7 @@
|
|||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#whats-new" class="md-nav__link">
|
<a href="#whats-new" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
📦 What’s New?
|
📦 What's New?
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -781,6 +781,15 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
@@ -1026,11 +1035,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1 id="use-case-9-nested-document-population-circularref-handling">Use Case 9: Nested Document Population & Circular‑Ref Handling</h1>
|
<h1 id="use-case-9-nested-document-population-circular-ref-handling">Use Case 9: Nested Document Population & Circular-Ref Handling</h1>
|
||||||
<p><strong>Scenario:</strong>
|
<p><strong>Scenario:</strong> An <code>Author</code> has <code>books</code> (a list of references), each <code>Book</code> references a <code>publisher</code>, and an <code>Author</code> may reference a <code>mentor</code> — which is <em>another</em> <code>Author</code> (a potential cycle).</p>
|
||||||
A service must return an <code>Author</code> object with its <code>books</code> populated, and each <code>book</code> must include its <code>publisher</code>. The data model contains a possible circular reference (<code>Author.mentor_id</code> → another <code>Author</code>).</p>
|
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="whats-new">📦 What’s New?</h2>
|
<h2 id="whats-new">📦 What's New?</h2>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -1040,117 +1048,120 @@ A service must return an <code>Author</code> object with its <code>books</code>
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong><code>PopulateRule</code> (nested)</strong></td>
|
<td><code>nested_rules</code></td>
|
||||||
<td>Allows you to declare a hierarchy of rules. In this example we populate <code>books</code> on <code>Author</code>, then <code>publisher</code> on each <code>Book</code>.</td>
|
<td>Populates deeper levels: resolve <code>books</code> on <code>Author</code>, then <code>publisher</code> inside each <code>Book</code>.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong><code>max_depth</code></strong></td>
|
<td><code>max_depth</code></td>
|
||||||
<td>Prevents infinite recursion when circular references exist.</td>
|
<td>Per-rule recursion bound — the safety net for cyclic graphs.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>Circular‑reference guard</strong></td>
|
<td><code>global_max_depth</code></td>
|
||||||
<td><code>PopulationEngine</code> raises <code>CircularReferenceError</code> if a cycle exceeds <code>max_depth</code> or is revisited.</td>
|
<td><code>PopulationEngine(repos, global_max_depth=10)</code> global cap.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>CircularReferenceError</code></td>
|
||||||
|
<td>Raised when a <code>(Class, id)</code> pair is revisited; carries the visited path.</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="example">🚀 Example</h2>
|
<h2 id="example">🚀 Example</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">PopulatingRepository</span><span class="p">,</span> <span class="n">ModelRegistry</span>
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.populate</span><span class="w"> </span><span class="kn">import</span> <span class="n">PopulateRule</span><span class="p">,</span> <span class="n">PopulationEngine</span>
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a>
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span><span class="p">,</span> <span class="n">HTTPException</span>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">ModelRegistry</span><span class="p">,</span> <span class="n">MongoConnectionManager</span><span class="p">,</span> <span class="n">PopulatingRepository</span>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="c1"># 1️⃣ Define the data models</span>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.cache</span><span class="w"> </span><span class="kn">import</span> <span class="n">CircularReferenceError</span>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.populate</span><span class="w"> </span><span class="kn">import</span> <span class="n">PopulateRule</span><span class="p">,</span> <span class="n">PopulationEngine</span>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="k">class</span><span class="w"> </span><span class="nc">Publisher</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="n">country</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a><span class="c1"># 1. Models — each ref field holds ObjectId(s) in the DB and becomes model(s) in memory.</span>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a>
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a><span class="k">class</span><span class="w"> </span><span class="nc">Publisher</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a><span class="k">class</span><span class="w"> </span><span class="nc">Book</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">country</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">publisher_id</span><span class="p">:</span> <span class="n">ObjectId</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Reference to Publisher</span>
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="n">publisher</span><span class="p">:</span> <span class="n">Publisher</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a>
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a><span class="k">class</span><span class="w"> </span><span class="nc">Book</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a><span class="k">class</span><span class="w"> </span><span class="nc">Author</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="n">publisher</span><span class="p">:</span> <span class="n">Publisher</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># ObjectId in DB, Publisher in memory</span>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> <span class="n">book_ids</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">ObjectId</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span> <span class="c1"># References to Book documents</span>
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> <span class="n">books</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">Book</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a>
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> <span class="n">mentor_id</span><span class="p">:</span> <span class="n">ObjectId</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Optional circular reference to another Author</span>
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="k">class</span><span class="w"> </span><span class="nc">Author</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="n">mentor</span><span class="p">:</span> <span class="s2">"Author"</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a>
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="n">books</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">Book</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># list[ObjectId] in DB, list[Book] in memory</span>
|
||||||
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="n">mentor</span><span class="p">:</span> <span class="s2">"Author"</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># self-reference — potential cycle</span>
|
||||||
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a><span class="c1"># 2️⃣ Create a PopulationEngine</span>
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a>
|
||||||
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a>
|
||||||
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a><span class="n">engine</span> <span class="o">=</span> <span class="n">PopulationEngine</span><span class="p">({})</span>
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a><span class="c1"># 2. Engine + nested rules.</span>
|
||||||
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a>
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a><span class="n">engine</span> <span class="o">=</span> <span class="n">PopulationEngine</span><span class="p">({})</span>
|
||||||
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a>
|
||||||
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a><span class="c1"># 3️⃣ Declare nested rules</span>
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a><span class="n">publisher_rule</span> <span class="o">=</span> <span class="n">PopulateRule</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a> <span class="n">field_name</span><span class="o">=</span><span class="s2">"publisher"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a><span class="c1"># Populate the Publisher inside each Book</span>
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"publishers"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a><span class="n">book_rule</span> <span class="o">=</span> <span class="n">PopulateRule</span><span class="p">(</span>
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a><span class="p">)</span>
|
||||||
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="n">field_name</span><span class="o">=</span><span class="s2">"publisher"</span><span class="p">,</span>
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a>
|
||||||
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"publishers"</span><span class="p">,</span>
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a><span class="n">book_rule</span> <span class="o">=</span> <span class="n">PopulateRule</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="n">ref_field</span><span class="o">=</span><span class="s2">"publisher_id"</span><span class="p">,</span>
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="n">field_name</span><span class="o">=</span><span class="s2">"books"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a><span class="p">)</span>
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"books"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a><span class="c1"># Populate books on Author, nesting the book_rule</span>
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="n">nested_rules</span><span class="o">=</span><span class="p">[</span><span class="n">publisher_rule</span><span class="p">],</span> <span class="c1"># fetch each book, then its publisher</span>
|
||||||
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a><span class="n">author_rule</span> <span class="o">=</span> <span class="n">PopulateRule</span><span class="p">(</span>
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> <span class="n">max_depth</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="n">field_name</span><span class="o">=</span><span class="s2">"books"</span><span class="p">,</span>
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a><span class="p">)</span>
|
||||||
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"books"</span><span class="p">,</span>
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a>
|
||||||
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="n">ref_field</span><span class="o">=</span><span class="s2">"book_ids"</span><span class="p">,</span>
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a><span class="n">mentor_rule</span> <span class="o">=</span> <span class="n">PopulateRule</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="n">nested_rules</span><span class="o">=</span><span class="p">[</span><span class="n">book_rule</span><span class="p">],</span>
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="n">field_name</span><span class="o">=</span><span class="s2">"mentor"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="n">max_depth</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="c1"># safety net for deep graphs</span>
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"authors"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a><span class="p">)</span>
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="n">max_depth</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="c1"># stops mentor chains early — also avoids unbounded cycles</span>
|
||||||
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a><span class="c1"># Optional mentor rule (demonstrates circular‑ref handling)</span>
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a><span class="p">)</span>
|
||||||
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a><span class="n">mentor_rule</span> <span class="o">=</span> <span class="n">PopulateRule</span><span class="p">(</span>
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a>
|
||||||
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="n">field_name</span><span class="o">=</span><span class="s2">"mentor"</span><span class="p">,</span>
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a>
|
||||||
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"authors"</span><span class="p">,</span>
|
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a><span class="k">class</span><span class="w"> </span><span class="nc">AuthorRepository</span><span class="p">(</span><span class="n">PopulatingRepository</span><span class="p">[</span><span class="n">Author</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="n">ref_field</span><span class="o">=</span><span class="s2">"mentor_id"</span><span class="p">,</span>
|
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a> <span class="n">max_depth</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span>
|
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a><span class="p">)</span>
|
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"authors"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a>
|
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a> <span class="n">model</span><span class="o">=</span><span class="n">Author</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="n">population_engine</span><span class="o">=</span><span class="n">engine</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a><span class="c1"># 4️⃣ Repository that uses the engine + rules</span>
|
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="n">populate_rules</span><span class="o">=</span><span class="p">[</span><span class="n">book_rule</span><span class="p">,</span> <span class="n">mentor_rule</span><span class="p">],</span>
|
||||||
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a> <span class="p">)</span>
|
||||||
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a><span class="k">class</span><span class="w"> </span><span class="nc">AuthorRepository</span><span class="p">(</span><span class="n">PopulatingRepository</span><span class="p">[</span><span class="n">Author</span><span class="p">]):</span>
|
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a>
|
||||||
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a>
|
||||||
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span>
|
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a><span class="nd">@asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"authors"</span><span class="p">,</span>
|
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">_app</span><span class="p">:</span> <span class="n">FastAPI</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a> <span class="n">model</span><span class="o">=</span><span class="n">Author</span><span class="p">,</span>
|
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a> <span class="n">population_engine</span><span class="o">=</span><span class="n">engine</span><span class="p">,</span>
|
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span> <span class="n">db_name</span><span class="o">=</span><span class="s2">"library"</span>
|
||||||
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a> <span class="n">populate_rules</span><span class="o">=</span><span class="p">[</span><span class="n">author_rule</span><span class="p">,</span> <span class="n">mentor_rule</span><span class="p">],</span>
|
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a> <span class="p">):</span>
|
||||||
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a> <span class="p">)</span>
|
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a> <span class="n">engine</span><span class="o">.</span><span class="n">register_repo</span><span class="p">(</span><span class="s2">"publishers"</span><span class="p">,</span> <span class="n">PopulatingRepository</span><span class="p">[</span><span class="n">Publisher</span><span class="p">](</span><span class="s2">"publishers"</span><span class="p">,</span> <span class="n">Publisher</span><span class="p">))</span>
|
||||||
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a>
|
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a> <span class="n">engine</span><span class="o">.</span><span class="n">register_repo</span><span class="p">(</span><span class="s2">"books"</span><span class="p">,</span> <span class="n">PopulatingRepository</span><span class="p">[</span><span class="n">Book</span><span class="p">](</span><span class="s2">"books"</span><span class="p">,</span> <span class="n">Book</span><span class="p">))</span>
|
||||||
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a> <span class="n">engine</span><span class="o">.</span><span class="n">register_repo</span><span class="p">(</span><span class="s2">"authors"</span><span class="p">,</span> <span class="n">AuthorRepository</span><span class="p">())</span>
|
||||||
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a><span class="c1"># 5️⃣ Register all repositories with the engine</span>
|
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a> <span class="k">yield</span>
|
||||||
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a><span class="n">engine</span><span class="o">.</span><span class="n">register_repo</span><span class="p">(</span><span class="s2">"publishers"</span><span class="p">,</span> <span class="n">PopulatingRepository</span><span class="p">[</span><span class="n">Publisher</span><span class="p">](</span><span class="s2">"publishers"</span><span class="p">,</span> <span class="n">Publisher</span><span class="p">))</span>
|
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a>
|
||||||
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a><span class="n">engine</span><span class="o">.</span><span class="n">register_repo</span><span class="p">(</span><span class="s2">"books"</span><span class="p">,</span> <span class="n">PopulatingRepository</span><span class="p">[</span><span class="n">Book</span><span class="p">](</span><span class="s2">"books"</span><span class="p">,</span> <span class="n">Book</span><span class="p">))</span>
|
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a>
|
||||||
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a><span class="n">engine</span><span class="o">.</span><span class="n">register_repo</span><span class="p">(</span><span class="s2">"authors"</span><span class="p">,</span> <span class="n">AuthorRepository</span><span class="p">())</span>
|
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a>
|
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a>
|
||||||
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a>
|
||||||
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a><span class="c1"># 6️⃣ FastAPI lifespan – initialise DB & cache (if any)</span>
|
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/authors/</span><span class="si">{author_id}</span><span class="s2">"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_author</span><span class="p">(</span><span class="n">author_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">MongoConnectionManager</span>
|
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a> <span class="k">try</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a> <span class="n">author</span> <span class="o">=</span> <span class="k">await</span> <span class="n">AuthorRepository</span><span class="p">()</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="n">author_id</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a>
|
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a> <span class="k">except</span> <span class="n">CircularReferenceError</span> <span class="k">as</span> <span class="n">exc</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-79" name="__codelineno-0-79" href="#__codelineno-0-79"></a><span class="nd">@asynccontextmanager</span>
|
<a id="__codelineno-0-79" name="__codelineno-0-79" href="#__codelineno-0-79"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">409</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="sa">f</span><span class="s2">"Circular reference: </span><span class="si">{</span><span class="n">exc</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-80" name="__codelineno-0-80" href="#__codelineno-0-80"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
|
<a id="__codelineno-0-80" name="__codelineno-0-80" href="#__codelineno-0-80"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">author</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-81" name="__codelineno-0-81" href="#__codelineno-0-81"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
<a id="__codelineno-0-81" name="__codelineno-0-81" href="#__codelineno-0-81"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"Author not found"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-82" name="__codelineno-0-82" href="#__codelineno-0-82"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span>
|
<a id="__codelineno-0-82" name="__codelineno-0-82" href="#__codelineno-0-82"></a> <span class="k">return</span> <span class="n">author</span>
|
||||||
<a id="__codelineno-0-83" name="__codelineno-0-83" href="#__codelineno-0-83"></a> <span class="n">db_name</span><span class="o">=</span><span class="s2">"library"</span><span class="p">,</span>
|
|
||||||
<a id="__codelineno-0-84" name="__codelineno-0-84" href="#__codelineno-0-84"></a> <span class="p">):</span>
|
|
||||||
<a id="__codelineno-0-85" name="__codelineno-0-85" href="#__codelineno-0-85"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
|
||||||
<a id="__codelineno-0-86" name="__codelineno-0-86" href="#__codelineno-0-86"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_cache</span><span class="p">()</span>
|
|
||||||
<a id="__codelineno-0-87" name="__codelineno-0-87" href="#__codelineno-0-87"></a> <span class="k">yield</span>
|
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="tips">💡 Tips</h2>
|
<h2 id="tips">💡 Tips</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Use <code>max_depth</code> conservatively; a value of <code>2‑3</code> is enough for most graphs.</li>
|
<li><code>CircularReferenceError</code> is a <code>ValueError</code> subtype exposing <code>.collection</code>, <code>.doc_id</code>, and the visited <code>.path</code> — use it in error responses and logging.</li>
|
||||||
<li>When you <em>know</em> there are no cycles, you can omit <code>max_depth</code> on nested rules.</li>
|
<li>Keep <code>max_depth</code> conservative (2–3) for most graphs; combine with the global <code>global_max_depth=10</code> default.</li>
|
||||||
<li>The <code>CircularReferenceError</code> includes the visited path, which helps debugging.</li>
|
<li>List refs depopulate back to <code>list[ObjectId]</code> on <code>create</code>/<code>update</code>, mirroring the scalar case.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../08_population/">08 – Population</a> · <a href="../10_cache_and_population/">10 – Cache + population</a> · <a href="../../06_error_handling/">Error Handling</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
<div data-md-component="skip">
|
<div data-md-component="skip">
|
||||||
|
|
||||||
|
|
||||||
<a href="#use-case-10-caching-population-readthrough-populated-docs" class="md-skip">
|
<a href="#use-case-10-caching-population-read-through-populated-on-read" class="md-skip">
|
||||||
Skip to content
|
Skip to content
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -782,7 +782,7 @@
|
|||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#whats-new" class="md-nav__link">
|
<a href="#whats-new" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
📦 What’s New?
|
📦 What's New?
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -804,6 +804,15 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
@@ -1026,11 +1035,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1 id="use-case-10-caching-population-readthrough-populated-docs">Use Case 10: Caching + Population (Read‑Through + Populated Docs)</h1>
|
<h1 id="use-case-10-caching-population-read-through-populated-on-read">Use Case 10: Caching + Population (Read-Through, Populated on Read)</h1>
|
||||||
<p><strong>Scenario:</strong>
|
<p><strong>Scenario:</strong> A service needs fast reads of a <code>User</code> <strong>and</strong> its related <code>Profile</code>. We want the cache-hit path to skip the DB entirely while still returning a populated document.</p>
|
||||||
A service needs fast reads of a <code>User</code> document <strong>and</strong> its related <code>Profile</code>. The repository should cache the final populated result so subsequent calls hit the cache directly.</p>
|
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="whats-new">📦 What’s New?</h2>
|
<h2 id="whats-new">📦 What's New?</h2>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -1040,116 +1048,139 @@ A service needs fast reads of a <code>User</code> document <strong>and</strong>
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong><code>CachedBaseRepository</code></strong></td>
|
<td><code>CachedBaseRepository</code></td>
|
||||||
<td>Provides transparent ID‑based caching for CRUD operations.</td>
|
<td>Provides the cache layer (<code>cache-first get_by_id</code>, invalidation on <code>update</code>/<code>delete</code>).</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong><code>PopulatingRepository</code></strong></td>
|
<td><code>PopulationEngine</code></td>
|
||||||
<td>Adds automatic population of referenced documents.</td>
|
<td>Provides the reference resolution.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>Combined usage</strong></td>
|
<td><strong>Composed subclass</strong></td>
|
||||||
<td>By inheriting from <code>CachedBaseRepository</code> <em>and</em> wiring a <code>PopulationEngine</code>, the repository caches the <strong>populated</strong> document, eliminating both DB and population overhead on cache hits.</td>
|
<td>A small <code>CachedBaseRepository</code> subclass adds two <code>_populate</code> helpers so cache hits return populated models.</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<blockquote>
|
||||||
|
<p>⚠️ <strong>Known limitation:</strong> there is <strong>no built-in <code>CachedPopulatingRepository</code></strong> in the library. This use case documents the composition. We cache the <strong>raw (depopulated)</strong> document — references stay <code>ObjectId</code>s until read time, so <code>Profile</code> changes are reflected on the next fetch (within TTL) and <code>update</code>/<code>delete</code> invalidation remains correct.</p>
|
||||||
|
</blockquote>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="example">🚀 Example</h2>
|
<h2 id="example">🚀 Example</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">CachedBaseRepository</span><span class="p">,</span> <span class="n">ModelRegistry</span>
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Any</span>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.populate</span><span class="w"> </span><span class="kn">import</span> <span class="n">PopulateRule</span><span class="p">,</span> <span class="n">PopulationEngine</span>
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.cache</span><span class="w"> </span><span class="kn">import</span> <span class="n">InMemoryCacheBackend</span>
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a>
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">ModelRegistry</span><span class="p">,</span> <span class="n">MongoConnectionManager</span>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="c1"># 1️⃣ Define models</span>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.cache</span><span class="w"> </span><span class="kn">import</span> <span class="n">CacheConfig</span><span class="p">,</span> <span class="n">CachedBaseRepository</span><span class="p">,</span> <span class="n">InMemoryCacheBackend</span><span class="p">,</span> <span class="n">decode_value</span>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.populate</span><span class="w"> </span><span class="kn">import</span> <span class="n">PopulateRule</span><span class="p">,</span> <span class="n">PopulationEngine</span>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="k">class</span><span class="w"> </span><span class="nc">Profile</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="n">avatar_url</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> <span class="n">bio</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a><span class="c1"># 1. Models — `profile` holds an ObjectId in DB, a Profile in memory.</span>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a>
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a><span class="k">class</span><span class="w"> </span><span class="nc">Profile</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">avatar_url</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">bio</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="n">profile_id</span><span class="p">:</span> <span class="n">ObjectId</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Reference to Profile</span>
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">profile</span><span class="p">:</span> <span class="n">Profile</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Populated field</span>
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a>
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a><span class="c1"># 2️⃣ Initialise cache backend (in‑memory example)</span>
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> <span class="n">profile</span><span class="p">:</span> <span class="n">Profile</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a>
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="n">cache</span> <span class="o">=</span> <span class="n">InMemoryCacheBackend</span><span class="p">(</span><span class="n">max_entries</span><span class="o">=</span><span class="mi">20_000</span><span class="p">,</span> <span class="n">default_ttl</span><span class="o">=</span><span class="mi">600</span><span class="p">)</span>
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a>
|
||||||
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">set_cache_backend</span><span class="p">(</span><span class="n">cache</span><span class="p">)</span>
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a><span class="c1"># 2. Engine + rule.</span>
|
||||||
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a>
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a><span class="n">engine</span> <span class="o">=</span> <span class="n">PopulationEngine</span><span class="p">({})</span>
|
||||||
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a><span class="n">profile_rule</span> <span class="o">=</span> <span class="n">PopulateRule</span><span class="p">(</span><span class="n">field_name</span><span class="o">=</span><span class="s2">"profile"</span><span class="p">,</span> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"profiles"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a><span class="c1"># 3️⃣ Set up a PopulationEngine (repositories will be registered later)</span>
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a>
|
||||||
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a>
|
||||||
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a><span class="n">engine</span> <span class="o">=</span> <span class="n">PopulationEngine</span><span class="p">({})</span>
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a><span class="c1"># 3. Composed repository.</span>
|
||||||
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a><span class="n">profile_rule</span> <span class="o">=</span> <span class="n">PopulateRule</span><span class="p">(</span>
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a><span class="k">class</span><span class="w"> </span><span class="nc">CachedUserRepository</span><span class="p">(</span><span class="n">CachedBaseRepository</span><span class="p">[</span><span class="n">User</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a> <span class="n">field_name</span><span class="o">=</span><span class="s2">"profile"</span><span class="p">,</span>
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"profiles"</span><span class="p">,</span>
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a> <span class="bp">self</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="n">ref_field</span><span class="o">=</span><span class="s2">"profile_id"</span><span class="p">,</span>
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="n">cache_backend</span><span class="p">:</span> <span class="n">InMemoryCacheBackend</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a><span class="p">)</span>
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="n">population_engine</span><span class="p">:</span> <span class="n">PopulationEngine</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a>
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="n">populate_rules</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">PopulateRule</span><span class="p">],</span>
|
||||||
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="n">config</span><span class="p">:</span> <span class="n">CacheConfig</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a><span class="c1"># 4️⃣ Cached + Populating repository</span>
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="p">):</span>
|
||||||
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">User</span><span class="p">,</span> <span class="n">cache_backend</span><span class="p">,</span> <span class="n">config</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a><span class="k">class</span><span class="w"> </span><span class="nc">UserRepository</span><span class="p">(</span><span class="n">CachedBaseRepository</span><span class="p">[</span><span class="n">User</span><span class="p">]):</span>
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="bp">self</span><span class="o">.</span><span class="n">population_engine</span> <span class="o">=</span> <span class="n">population_engine</span>
|
||||||
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> <span class="bp">self</span><span class="o">.</span><span class="n">_populate_rules</span> <span class="o">=</span> <span class="n">populate_rules</span>
|
||||||
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span>
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a>
|
||||||
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"users"</span><span class="p">,</span>
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">_populate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">])</span> <span class="o">-></span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]:</span>
|
||||||
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="n">model</span><span class="o">=</span><span class="n">User</span><span class="p">,</span>
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a><span class="w"> </span><span class="sd">"""Dict-level resolution (mirrors PopulatingRepository._populate)."""</span>
|
||||||
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="n">cache_backend</span><span class="o">=</span><span class="n">cache</span><span class="p">,</span>
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="k">for</span> <span class="n">rule</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_populate_rules</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="n">config</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="c1"># defaults to enabled=True, backend="memory"</span>
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="n">ref</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">rule</span><span class="o">.</span><span class="n">field_name</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="p">)</span>
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="k">if</span> <span class="n">ref</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="c1"># Attach the population engine after the base repo is ready</span>
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="k">continue</span>
|
||||||
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="bp">self</span><span class="o">.</span><span class="n">population_engine</span> <span class="o">=</span> <span class="n">engine</span>
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">ref</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span> <span class="c1"># JSON round-trip turns ObjectId into hex str</span>
|
||||||
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="bp">self</span><span class="o">.</span><span class="n">_populate_rules</span> <span class="o">=</span> <span class="p">[</span><span class="n">profile_rule</span><span class="p">]</span>
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="n">ref</span> <span class="o">=</span> <span class="n">ObjectId</span><span class="p">(</span><span class="n">ref</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a>
|
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">ref</span><span class="p">,</span> <span class="nb">list</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="c1"># Override get_by_id to include population before caching</span>
|
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="n">resolved</span> <span class="o">=</span> <span class="p">[]</span>
|
||||||
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_by_id</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">):</span>
|
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">ref</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a> <span class="c1"># First attempt cache lookup (as in CachedBaseRepository)</span>
|
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a> <span class="n">item</span> <span class="o">=</span> <span class="n">ObjectId</span><span class="p">(</span><span class="n">item</span><span class="p">)</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="nb">str</span><span class="p">)</span> <span class="k">else</span> <span class="n">item</span>
|
||||||
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a> <span class="n">cached</span> <span class="o">=</span> <span class="k">await</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
|
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a> <span class="n">repo</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">population_engine</span><span class="o">.</span><span class="n">_repos</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">rule</span><span class="o">.</span><span class="n">collection_name</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="k">if</span> <span class="n">cached</span><span class="p">:</span>
|
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="n">doc</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="n">item</span><span class="p">)</span> <span class="k">if</span> <span class="n">repo</span> <span class="k">else</span> <span class="kc">None</span>
|
||||||
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="k">return</span> <span class="n">cached</span>
|
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="n">resolved</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">doc</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a> <span class="c1"># Not in cache – fetch from DB and populate</span>
|
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a> <span class="n">data</span><span class="p">[</span><span class="n">rule</span><span class="o">.</span><span class="n">field_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">resolved</span>
|
||||||
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a> <span class="n">doc</span> <span class="o">=</span> <span class="k">await</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span> <span class="c1"># this will hit the DB (no cache hit)</span>
|
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a> <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">ref</span><span class="p">,</span> <span class="n">ObjectId</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> <span class="k">if</span> <span class="n">doc</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> <span class="n">repo</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">population_engine</span><span class="o">.</span><span class="n">_repos</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">rule</span><span class="o">.</span><span class="n">collection_name</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> <span class="k">return</span> <span class="kc">None</span>
|
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> <span class="n">data</span><span class="p">[</span><span class="n">rule</span><span class="o">.</span><span class="n">field_name</span><span class="p">]</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="n">ref</span><span class="p">)</span> <span class="k">if</span> <span class="n">repo</span> <span class="k">else</span> <span class="kc">None</span>
|
||||||
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a> <span class="c1"># Populate related docs</span>
|
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a> <span class="k">return</span> <span class="n">data</span>
|
||||||
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a> <span class="n">populated</span> <span class="o">=</span> <span class="k">await</span> <span class="n">engine</span><span class="o">.</span><span class="n">populate</span><span class="p">(</span><span class="n">doc</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_populate_rules</span><span class="p">)</span>
|
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a>
|
||||||
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a> <span class="c1"># Store the fully populated result in cache for next call</span>
|
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_by_id</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">_cache</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_cache_key</span><span class="p">(</span><span class="nb">id</span><span class="p">),</span> <span class="n">populated</span><span class="p">)</span>
|
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a> <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_cache_config</span><span class="o">.</span><span class="n">enabled</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a> <span class="k">return</span> <span class="n">populated</span>
|
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a> <span class="k">return</span> <span class="k">await</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span> <span class="c1"># no cache -> raw, unpopulated</span>
|
||||||
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a>
|
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a>
|
||||||
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a> <span class="n">cached</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">_cache</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_cache_key</span><span class="p">(</span><span class="nb">id</span><span class="p">))</span>
|
||||||
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a><span class="c1"># 5️⃣ Register repositories with the engine for population resolution</span>
|
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a> <span class="k">if</span> <span class="n">cached</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a> <span class="c1"># Cache hit: decode the RAW doc, then populate before returning.</span>
|
||||||
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a><span class="n">engine</span><span class="o">.</span><span class="n">register_repo</span><span class="p">(</span><span class="s2">"profiles"</span><span class="p">,</span> <span class="n">PopulatingRepository</span><span class="p">[</span><span class="n">Profile</span><span class="p">](</span><span class="s2">"profiles"</span><span class="p">,</span> <span class="n">Profile</span><span class="p">))</span>
|
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a> <span class="n">data</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">_populate</span><span class="p">(</span><span class="n">decode_value</span><span class="p">(</span><span class="n">cached</span><span class="p">))</span>
|
||||||
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a><span class="n">engine</span><span class="o">.</span><span class="n">register_repo</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">UserRepository</span><span class="p">())</span>
|
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="p">(</span><span class="o">**</span><span class="n">data</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a>
|
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a>
|
||||||
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a> <span class="c1"># Cache miss: single DB read; the base class caches the raw (depopulated) doc.</span>
|
||||||
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a><span class="c1"># 6️⃣ FastAPI lifespan – initialise DB, create indexes, and start cache cleanup</span>
|
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a> <span class="k">if</span> <span class="n">result</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">MongoConnectionManager</span>
|
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a> <span class="k">return</span> <span class="kc">None</span>
|
||||||
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="p">(</span><span class="o">**</span><span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">_populate</span><span class="p">(</span><span class="n">result</span><span class="o">.</span><span class="n">model_dump</span><span class="p">(</span><span class="n">by_alias</span><span class="o">=</span><span class="kc">True</span><span class="p">)))</span>
|
||||||
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a>
|
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a>
|
||||||
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a><span class="nd">@asynccontextmanager</span>
|
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a>
|
||||||
<a id="__codelineno-0-79" name="__codelineno-0-79" href="#__codelineno-0-79"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
|
<a id="__codelineno-0-79" name="__codelineno-0-79" href="#__codelineno-0-79"></a><span class="c1"># 4. Wire-up — one backend for both the repo and the registry lifecycle.</span>
|
||||||
<a id="__codelineno-0-80" name="__codelineno-0-80" href="#__codelineno-0-80"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
<a id="__codelineno-0-80" name="__codelineno-0-80" href="#__codelineno-0-80"></a><span class="n">cache</span> <span class="o">=</span> <span class="n">InMemoryCacheBackend</span><span class="p">(</span><span class="n">max_entries</span><span class="o">=</span><span class="mi">20_000</span><span class="p">,</span> <span class="n">default_ttl</span><span class="o">=</span><span class="mi">600</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-81" name="__codelineno-0-81" href="#__codelineno-0-81"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span>
|
<a id="__codelineno-0-81" name="__codelineno-0-81" href="#__codelineno-0-81"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">set_cache_backend</span><span class="p">(</span><span class="n">cache</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-82" name="__codelineno-0-82" href="#__codelineno-0-82"></a> <span class="n">db_name</span><span class="o">=</span><span class="s2">"app_db"</span><span class="p">,</span>
|
<a id="__codelineno-0-82" name="__codelineno-0-82" href="#__codelineno-0-82"></a>
|
||||||
<a id="__codelineno-0-83" name="__codelineno-0-83" href="#__codelineno-0-83"></a> <span class="p">):</span>
|
<a id="__codelineno-0-83" name="__codelineno-0-83" href="#__codelineno-0-83"></a>
|
||||||
<a id="__codelineno-0-84" name="__codelineno-0-84" href="#__codelineno-0-84"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
<a id="__codelineno-0-84" name="__codelineno-0-84" href="#__codelineno-0-84"></a><span class="nd">@asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-85" name="__codelineno-0-85" href="#__codelineno-0-85"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_cache</span><span class="p">()</span>
|
<a id="__codelineno-0-85" name="__codelineno-0-85" href="#__codelineno-0-85"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">_app</span><span class="p">:</span> <span class="n">FastAPI</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-86" name="__codelineno-0-86" href="#__codelineno-0-86"></a> <span class="k">yield</span>
|
<a id="__codelineno-0-86" name="__codelineno-0-86" href="#__codelineno-0-86"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
||||||
|
<a id="__codelineno-0-87" name="__codelineno-0-87" href="#__codelineno-0-87"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span> <span class="n">db_name</span><span class="o">=</span><span class="s2">"app_db"</span>
|
||||||
|
<a id="__codelineno-0-88" name="__codelineno-0-88" href="#__codelineno-0-88"></a> <span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-89" name="__codelineno-0-89" href="#__codelineno-0-89"></a> <span class="c1"># Register the repositories the engine resolves refs against:</span>
|
||||||
|
<a id="__codelineno-0-90" name="__codelineno-0-90" href="#__codelineno-0-90"></a> <span class="c1"># engine.register_repo("profiles", ProfileRepo())</span>
|
||||||
|
<a id="__codelineno-0-91" name="__codelineno-0-91" href="#__codelineno-0-91"></a> <span class="n">engine</span><span class="o">.</span><span class="n">register_repo</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">CachedUserRepository</span><span class="p">(</span><span class="n">cache</span><span class="p">,</span> <span class="n">engine</span><span class="p">,</span> <span class="p">[</span><span class="n">profile_rule</span><span class="p">]))</span>
|
||||||
|
<a id="__codelineno-0-92" name="__codelineno-0-92" href="#__codelineno-0-92"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
||||||
|
<a id="__codelineno-0-93" name="__codelineno-0-93" href="#__codelineno-0-93"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_cache</span><span class="p">()</span>
|
||||||
|
<a id="__codelineno-0-94" name="__codelineno-0-94" href="#__codelineno-0-94"></a> <span class="k">yield</span>
|
||||||
|
<a id="__codelineno-0-95" name="__codelineno-0-95" href="#__codelineno-0-95"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">shutdown_cache</span><span class="p">()</span>
|
||||||
|
<a id="__codelineno-0-96" name="__codelineno-0-96" href="#__codelineno-0-96"></a>
|
||||||
|
<a id="__codelineno-0-97" name="__codelineno-0-97" href="#__codelineno-0-97"></a>
|
||||||
|
<a id="__codelineno-0-98" name="__codelineno-0-98" href="#__codelineno-0-98"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">(</span><span class="n">lifespan</span><span class="o">=</span><span class="n">lifespan</span><span class="p">)</span>
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
|
<blockquote>
|
||||||
|
<p><strong>Note:</strong> replace the placeholder <code>engine.register_repo("profiles", ...)</code> line with a repository for <code>Profile</code>. A matching <code>CachedBaseRepository[Profile]("profiles", Profile, cache)</code> keeps profile reads cached too — e.g. <code>engine.register_repo("profiles", CachedBaseRepository[Profile]("profiles", Profile, cache))</code>.</p>
|
||||||
|
</blockquote>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="tips">💡 Tips</h2>
|
<h2 id="tips">💡 Tips</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>The example overrides <code>get_by_id</code> to keep the caching logic simple; you could also create a mixin that composes <code>CachedBaseRepository</code> and <code>PopulatingRepository</code>.</li>
|
<li><strong>Cache the raw doc, populate at read time.</strong> Ref fields stay <code>ObjectId</code>s in the cache; JSON round-trips them to hex strings, so <code>_populate</code> must normalize <code>str → ObjectId</code> (shown above).</li>
|
||||||
<li>Remember to call <code>await ModelRegistry.initialize_cache()</code> <strong>after</strong> the DB connection is ready; otherwise the background cleanup task will never start.</li>
|
<li><code>update</code>/<code>delete</code> from the base class still invalidate the right key because we never repopulate <em>in</em> the cache.</li>
|
||||||
<li>Cache keys are generated by <code>CachedBaseRepository._cache_key(id)</code>. If you change the repository’s <code>config.key_prefix</code>, the same prefix will be used for populated results.</li>
|
<li>Set a sensible <code>default_ttl</code> — cached <code>User</code> entries resolve <code>Profile</code> on each read, so profile edits show up within the TTL (or call <code>invalidate_cache(user_id)</code> explicitly).</li>
|
||||||
|
<li>If you want one repo class used app-wide, keep the populate helpers in a mixin shared with regular <code>PopulatingRepository</code>.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../07_caching/">07 – Caching</a> · <a href="../08_population/">08 – Population</a> · <a href="../11_cache_lifecycle/">11 – Cache lifecycle</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -805,7 +805,7 @@
|
|||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#whats-new" class="md-nav__link">
|
<a href="#whats-new" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
📦 What’s New?
|
📦 What's New?
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -818,20 +818,14 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<nav class="md-nav" aria-label="🚀 Example">
|
|
||||||
<ul class="md-nav__list">
|
|
||||||
|
|
||||||
<li class="md-nav__item">
|
|
||||||
<a href="#why-a-separate-shutdown-step" class="md-nav__link">
|
|
||||||
<span class="md-ellipsis">
|
|
||||||
Why a separate shutdown step?
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
<li class="md-nav__item">
|
||||||
</nav>
|
<a href="#what-actually-happens" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
🔁 What Actually Happens
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
@@ -842,6 +836,15 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
@@ -1042,10 +1045,9 @@
|
|||||||
|
|
||||||
|
|
||||||
<h1 id="use-case-11-proper-cache-lifecycle-in-fastapi">Use Case 11: Proper Cache Lifecycle in FastAPI</h1>
|
<h1 id="use-case-11-proper-cache-lifecycle-in-fastapi">Use Case 11: Proper Cache Lifecycle in FastAPI</h1>
|
||||||
<p><strong>Scenario:</strong>
|
<p><strong>Scenario:</strong> A microservice uses an in-memory (or Redis) cache backend and must start the cleanup task on app start, then shut it down cleanly on termination.</p>
|
||||||
A microservice uses <code>InMemoryCacheBackend</code> (or Redis) and needs to start the cache cleanup task when the app starts, then shut it down cleanly on termination.</p>
|
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="whats-new">📦 What’s New?</h2>
|
<h2 id="whats-new">📦 What's New?</h2>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -1055,55 +1057,69 @@ A microservice uses <code>InMemoryCacheBackend</code> (or Redis) and needs to st
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td><code>ModelRegistry.set_cache_backend(backend)</code></td>
|
||||||
|
<td>Registers the single shared backend.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td><code>ModelRegistry.initialize_cache()</code></td>
|
<td><code>ModelRegistry.initialize_cache()</code></td>
|
||||||
<td>Starts the async background task for the registered cache backend.</td>
|
<td>Starts the backend (spawns the in-memory TTL cleanup task). Raises <code>RuntimeError</code> if no backend was registered.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>ModelRegistry.shutdown_cache()</code></td>
|
<td><code>ModelRegistry.shutdown_cache()</code></td>
|
||||||
<td>Gracefully stops the background task and releases resources.</td>
|
<td>Cancels the cleanup task, closes pub/sub, clears the backend.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>FastAPI lifespan integration</td>
|
<td>If a repo uses a backend directly</td>
|
||||||
<td>Demonstrates where to call both init and shutdown methods.</td>
|
<td>Same instance must be registered so <code>initialize_cache</code> starts <em>its</em> task.</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="example">🚀 Example</h2>
|
<h2 id="example">🚀 Example</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">MongoConnectionManager</span><span class="p">,</span> <span class="n">ModelRegistry</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.cache</span><span class="w"> </span><span class="kn">import</span> <span class="n">InMemoryCacheBackend</span>
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a>
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">ModelRegistry</span><span class="p">,</span> <span class="n">MongoConnectionManager</span>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="c1"># Initialise a cache backend (in‑memory example)</span>
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.cache</span><span class="w"> </span><span class="kn">import</span> <span class="n">InMemoryCacheBackend</span>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="n">cache</span> <span class="o">=</span> <span class="n">InMemoryCacheBackend</span><span class="p">(</span><span class="n">max_entries</span><span class="o">=</span><span class="mi">10_000</span><span class="p">,</span> <span class="n">default_ttl</span><span class="o">=</span><span class="mi">300</span><span class="p">)</span>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">set_cache_backend</span><span class="p">(</span><span class="n">cache</span><span class="p">)</span>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="n">cache</span> <span class="o">=</span> <span class="n">InMemoryCacheBackend</span><span class="p">(</span><span class="n">max_entries</span><span class="o">=</span><span class="mi">10_000</span><span class="p">,</span> <span class="n">default_ttl</span><span class="o">=</span><span class="mi">300</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">set_cache_backend</span><span class="p">(</span><span class="n">cache</span><span class="p">)</span> <span class="c1"># before any cache-backed repo is used</span>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="nd">@asynccontextmanager</span>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> <span class="c1"># Start MongoDB connection and cache background task</span>
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a><span class="nd">@asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">_app</span><span class="p">:</span> <span class="n">FastAPI</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span>
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">db_name</span><span class="o">=</span><span class="s2">"mydb"</span><span class="p">,</span>
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span> <span class="n">db_name</span><span class="o">=</span><span class="s2">"mydb"</span>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="p">):</span>
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="p">):</span>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span> <span class="c1"># create indexes</span>
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span> <span class="c1"># create indexes (idempotent)</span>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_cache</span><span class="p">()</span> <span class="c1"># start cache cleanup</span>
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_cache</span><span class="p">()</span> <span class="c1"># start the TTL cleanup task</span>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="k">yield</span>
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="k">yield</span>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> <span class="c1"># FastAPI will exit the `with` block here – clean up cache</span>
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">shutdown_cache</span><span class="p">()</span> <span class="c1"># cancel task + close cleanly</span>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">shutdown_cache</span><span class="p">()</span>
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a>
|
||||||
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a>
|
||||||
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">(</span><span class="n">lifespan</span><span class="o">=</span><span class="n">lifespan</span><span class="p">)</span>
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
<h3 id="why-a-separate-shutdown-step">Why a separate shutdown step?</h3>
|
<hr />
|
||||||
|
<h2 id="what-actually-happens">🔁 What Actually Happens</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>The cache backend may spawn an <code>asyncio.Task</code> that periodically removes expired entries. If the task is left dangling, the event loop may complain about pending tasks on shutdown.</li>
|
<li><code>InMemoryCacheBackend.initialize()</code> spawns an <code>asyncio.Task</code> that evicts expired entries every <code>cleanup_interval</code> seconds. Without a shutdown, the event loop flags the dangling task on exit — <code>shutdown_cache()</code> cancels it and awaits it.</li>
|
||||||
<li><code>shutdown_cache()</code> cancels the internal task and waits for it to finish, ensuring a clean exit.</li>
|
<li><code>shutdown_cache()</code> also NULs the registry cache backend and closes the Redis pub/sub handle (if Redis).</li>
|
||||||
|
<li>If <code>initialize_cache()</code> is called before <code>set_cache_backend()</code>, it raises:</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div class="language-text highlight"><span class="filename">Text Only</span><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a>RuntimeError: No cache backend registered. Call set_cache_backend() first.
|
||||||
|
</code></pre></div>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="tips">💡 Tips</h2>
|
<h2 id="tips">💡 Tips</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Register the cache <strong>before</strong> calling <code>initialize_cache()</code>; otherwise the registry won’t know which backend to start.</li>
|
<li>Per-repository toggling is independent of the lifecycle: <code>CacheConfig(enabled=False)</code> bypasses the cache for that repo even after <code>initialize_cache()</code>.</li>
|
||||||
<li>For Redis backends, the shutdown step also closes the underlying <code>redis.asyncio.Redis</code> client connection.</li>
|
<li>The cleanup task uses <code>cleanup_interval</code> seconds for scans; entries also expire on access via the TTL heap (<code>default_ttl=0</code> expires immediately).</li>
|
||||||
<li>You can also hook the shutdown into a FastAPI <code>@app.on_event("shutdown")</code> handler if you prefer not to use the lifespan context manager.</li>
|
<li>Register the backend <strong>before</strong> constructing any <code>CachedBaseRepository</code> that references it — otherwise a repo may hold an uninitialized backend (no cleanup task, no Redis pub/sub).</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../07_caching/">07 – Caching</a> · <a href="../../02_components/">Components: ModelRegistry</a> · <a href="../10_cache_and_population/">10 – Cache + population</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -828,7 +828,7 @@
|
|||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#whats-new" class="md-nav__link">
|
<a href="#whats-new" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
📦 What’s New?
|
📦 What's New?
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -850,6 +850,15 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
@@ -1027,10 +1036,9 @@
|
|||||||
|
|
||||||
|
|
||||||
<h1 id="use-case-12-using-transactionmanagerexecute_transaction">Use Case 12: Using <code>TransactionManager.execute_transaction</code></h1>
|
<h1 id="use-case-12-using-transactionmanagerexecute_transaction">Use Case 12: Using <code>TransactionManager.execute_transaction</code></h1>
|
||||||
<p><strong>Scenario:</strong>
|
<p><strong>Scenario:</strong> Perform several writes across different collections atomically — e.g., create an <code>Order</code> <strong>and</strong> decrement <code>Inventory</code>. The low-level <code>start_session</code> context works, but <code>execute_transaction</code> collects results from a list of async operations.</p>
|
||||||
You need to perform several writes across different collections atomically (e.g., creating an <code>Order</code> and updating the <code>Inventory</code> for each ordered item). The low‑level <code>start_session</code> context manager works, but the high‑level <code>execute_transaction</code> helper makes the code cleaner and returns all operation results in a list.</p>
|
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="whats-new">📦 What’s New?</h2>
|
<h2 id="whats-new">📦 What's New?</h2>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -1041,112 +1049,113 @@ You need to perform several writes across different collections atomically (e.g.
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>TransactionManager.execute_transaction</code></td>
|
<td><code>TransactionManager.execute_transaction</code></td>
|
||||||
<td>Accepts a list of async callables (each receiving a <code>session</code>) and executes them inside a single MongoDB transaction. Returns a list of each callable’s result.</td>
|
<td>Runs a list of async callables (each receives a session) inside one transaction; returns the result of each callable, in order.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Automatic rollback</td>
|
<td>Automatic rollback</td>
|
||||||
<td>If any operation raises an exception, the transaction is aborted and the exception propagates.</td>
|
<td>Any raised exception aborts the transaction and propagates to the caller.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>start_session</code></td>
|
||||||
|
<td>The underlying async context manager <code>async with TransactionManager.start_session() as session:</code>.</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="example">🚀 Example</h2>
|
<h2 id="example">🚀 Example</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">MongoConnectionManager</span><span class="p">,</span> <span class="n">ModelRegistry</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.transactions</span><span class="w"> </span><span class="kn">import</span> <span class="n">TransactionManager</span>
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">datetime</span><span class="w"> </span><span class="kn">import</span> <span class="n">datetime</span>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.repository</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseRepository</span>
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.models</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span>
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span><span class="p">,</span> <span class="n">HTTPException</span>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">ModelRegistry</span><span class="p">,</span> <span class="n">MongoConnectionManager</span>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.repository</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseRepository</span>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="c1"># 1️⃣ Define simple models</span>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.transactions</span><span class="w"> </span><span class="kn">import</span> <span class="n">TransactionManager</span>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a><span class="k">class</span><span class="w"> </span><span class="nc">Order</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> <span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span>
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a><span class="c1"># 1. Models</span>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> <span class="n">items</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">dict</span><span class="p">]</span> <span class="c1"># [{"product_id": ObjectId, "qty": int}]</span>
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a><span class="k">class</span><span class="w"> </span><span class="nc">Order</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">total</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="mf">0.0</span>
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a>
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">items</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">dict</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span> <span class="c1"># [{"product_id": ObjectId, "qty": int}]</span>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a><span class="k">class</span><span class="w"> </span><span class="nc">Inventory</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="n">total</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="mf">0.0</span>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="n">product_id</span><span class="p">:</span> <span class="n">ObjectId</span>
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">quantity</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a>
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a><span class="k">class</span><span class="w"> </span><span class="nc">Inventory</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> <span class="n">product_id</span><span class="p">:</span> <span class="n">ObjectId</span> <span class="o">=</span> <span class="kc">None</span>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a><span class="c1"># 2️⃣ Repositories</span>
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> <span class="n">quantity</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a>
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="k">class</span><span class="w"> </span><span class="nc">OrderRepo</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Order</span><span class="p">]):</span>
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a>
|
||||||
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a><span class="c1"># 2. Repositories (constructed inside the lifespan — after connect()).</span>
|
||||||
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"orders"</span><span class="p">,</span> <span class="n">Order</span><span class="p">)</span>
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a><span class="k">class</span><span class="w"> </span><span class="nc">OrderRepo</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Order</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a>
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a><span class="k">class</span><span class="w"> </span><span class="nc">InventoryRepo</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Inventory</span><span class="p">]):</span>
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"orders"</span><span class="p">,</span> <span class="n">Order</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a>
|
||||||
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"inventory"</span><span class="p">,</span> <span class="n">Inventory</span><span class="p">)</span>
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a>
|
||||||
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a>
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a><span class="k">class</span><span class="w"> </span><span class="nc">InventoryRepo</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Inventory</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a><span class="n">order_repo</span> <span class="o">=</span> <span class="n">OrderRepo</span><span class="p">()</span>
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a><span class="n">inv_repo</span> <span class="o">=</span> <span class="n">InventoryRepo</span><span class="p">()</span>
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"inventory"</span><span class="p">,</span> <span class="n">Inventory</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a>
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a>
|
||||||
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a>
|
||||||
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a><span class="c1"># 3️⃣ Transaction helper</span>
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a><span class="c1"># 3. The atomic operation.</span>
|
||||||
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_order_with_inventory</span><span class="p">(</span><span class="n">order</span><span class="p">:</span> <span class="n">Order</span><span class="p">,</span> <span class="n">order_repo</span><span class="p">:</span> <span class="n">OrderRepo</span><span class="p">,</span> <span class="n">inv_repo</span><span class="p">:</span> <span class="n">InventoryRepo</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_order_with_inventory</span><span class="p">(</span><span class="n">order</span><span class="p">:</span> <span class="n">Order</span><span class="p">):</span>
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">insert_order</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">insert_order</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="n">doc</span> <span class="o">=</span> <span class="n">order</span><span class="o">.</span><span class="n">model_dump</span><span class="p">(</span><span class="n">exclude</span><span class="o">=</span><span class="p">{</span><span class="s2">"id"</span><span class="p">},</span> <span class="n">exclude_none</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="c1"># Insert the order document</span>
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="n">doc</span><span class="p">[</span><span class="s2">"created_at"</span><span class="p">]</span> <span class="o">=</span> <span class="n">doc</span><span class="p">[</span><span class="s2">"updated_at"</span><span class="p">]</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> <span class="n">doc</span> <span class="o">=</span> <span class="n">order</span><span class="o">.</span><span class="n">model_dump</span><span class="p">(</span><span class="n">exclude</span><span class="o">=</span><span class="p">{</span><span class="s2">"id"</span><span class="p">},</span> <span class="n">exclude_none</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">order_repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">insert_one</span><span class="p">(</span><span class="n">doc</span><span class="p">,</span> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="n">doc</span><span class="p">[</span><span class="s2">"created_at"</span><span class="p">]</span> <span class="o">=</span> <span class="n">doc</span><span class="p">[</span><span class="s2">"updated_at"</span><span class="p">]</span> <span class="o">=</span> <span class="nb">__import__</span><span class="p">(</span><span class="s2">"datetime"</span><span class="p">)</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()</span>
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">order_repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">find_one</span><span class="p">({</span><span class="s2">"_id"</span><span class="p">:</span> <span class="n">result</span><span class="o">.</span><span class="n">inserted_id</span><span class="p">},</span> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">order_repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">insert_one</span><span class="p">(</span><span class="n">doc</span><span class="p">,</span> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">)</span>
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a>
|
||||||
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">order_repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">find_one</span><span class="p">({</span><span class="s2">"_id"</span><span class="p">:</span> <span class="n">result</span><span class="o">.</span><span class="n">inserted_id</span><span class="p">},</span> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">)</span>
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_inventory</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a>
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">order</span><span class="o">.</span><span class="n">items</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_inventory</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="k">await</span> <span class="n">inv_repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">update_one</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="c1"># Decrease quantity for each ordered item</span>
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="p">{</span><span class="s2">"product_id"</span><span class="p">:</span> <span class="n">ObjectId</span><span class="p">(</span><span class="n">item</span><span class="p">[</span><span class="s2">"product_id"</span><span class="p">])},</span>
|
||||||
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">order</span><span class="o">.</span><span class="n">items</span><span class="p">:</span>
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="p">{</span><span class="s2">"$inc"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"quantity"</span><span class="p">:</span> <span class="o">-</span><span class="n">item</span><span class="p">[</span><span class="s2">"qty"</span><span class="p">]}},</span>
|
||||||
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="k">await</span> <span class="n">inv_repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">update_one</span><span class="p">(</span>
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="p">{</span><span class="s2">"product_id"</span><span class="p">:</span> <span class="n">ObjectId</span><span class="p">(</span><span class="n">item</span><span class="p">[</span><span class="s2">"product_id"</span><span class="p">])},</span>
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="p">)</span>
|
||||||
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="p">{</span><span class="s2">"$inc"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"quantity"</span><span class="p">:</span> <span class="o">-</span><span class="n">item</span><span class="p">[</span><span class="s2">"qty"</span><span class="p">]}},</span>
|
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="k">return</span> <span class="s2">"inventory-updated"</span>
|
||||||
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span>
|
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a>
|
||||||
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a> <span class="p">)</span>
|
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a> <span class="n">results</span> <span class="o">=</span> <span class="k">await</span> <span class="n">TransactionManager</span><span class="o">.</span><span class="n">execute_transaction</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a> <span class="k">return</span> <span class="s2">"inventory-updated"</span>
|
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a> <span class="p">[</span><span class="n">insert_order</span><span class="p">,</span> <span class="n">update_inventory</span><span class="p">]</span>
|
||||||
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a>
|
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a> <span class="p">)</span>
|
||||||
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="c1"># Execute both operations atomically</span>
|
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="k">return</span> <span class="n">results</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># created order document; results[1] == "inventory-updated"</span>
|
||||||
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="n">results</span> <span class="o">=</span> <span class="k">await</span> <span class="n">TransactionManager</span><span class="o">.</span><span class="n">execute_transaction</span><span class="p">([</span>
|
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a>
|
||||||
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a> <span class="k">lambda</span> <span class="n">s</span><span class="p">:</span> <span class="n">insert_order</span><span class="p">(</span><span class="n">s</span><span class="p">),</span>
|
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a>
|
||||||
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a> <span class="k">lambda</span> <span class="n">s</span><span class="p">:</span> <span class="n">update_inventory</span><span class="p">(</span><span class="n">s</span><span class="p">),</span>
|
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a><span class="c1"># 4. FastAPI wiring.</span>
|
||||||
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> <span class="p">])</span>
|
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> <span class="c1"># `results[0]` is the created order document, `results[1]` is the marker string</span>
|
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a>
|
||||||
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a> <span class="k">return</span> <span class="n">results</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a>
|
||||||
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a>
|
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a><span class="nd">@asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">_app</span><span class="p">:</span> <span class="n">FastAPI</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a><span class="c1"># 4️⃣ FastAPI lifespan – initialise DB and run the example endpoint</span>
|
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span> <span class="n">db_name</span><span class="o">=</span><span class="s2">"shop"</span>
|
||||||
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span><span class="p">,</span> <span class="n">HTTPException</span>
|
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a> <span class="p">):</span>
|
||||||
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a>
|
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a> <span class="k">yield</span>
|
||||||
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">()</span>
|
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a>
|
||||||
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a>
|
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a>
|
||||||
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a><span class="nd">@asynccontextmanager</span>
|
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a><span class="n">app</span><span class="o">.</span><span class="n">lifespan</span> <span class="o">=</span> <span class="n">lifespan</span>
|
||||||
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
|
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a>
|
||||||
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a>
|
||||||
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span>
|
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/orders/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">Order</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a> <span class="n">db_name</span><span class="o">=</span><span class="s2">"shop"</span><span class="p">,</span>
|
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_order</span><span class="p">(</span><span class="n">order</span><span class="p">:</span> <span class="n">Order</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a> <span class="p">):</span>
|
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a> <span class="n">created</span> <span class="o">=</span> <span class="k">await</span> <span class="n">create_order_with_inventory</span><span class="p">(</span><span class="n">order</span><span class="p">,</span> <span class="n">OrderRepo</span><span class="p">(),</span> <span class="n">InventoryRepo</span><span class="p">())</span>
|
||||||
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a> <span class="k">if</span> <span class="n">created</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a> <span class="k">yield</span>
|
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"Transaction failed"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a>
|
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a> <span class="k">return</span> <span class="n">created</span>
|
||||||
<a id="__codelineno-0-79" name="__codelineno-0-79" href="#__codelineno-0-79"></a><span class="n">app</span><span class="o">.</span><span class="n">lifespan</span> <span class="o">=</span> <span class="n">lifespan</span>
|
|
||||||
<a id="__codelineno-0-80" name="__codelineno-0-80" href="#__codelineno-0-80"></a>
|
|
||||||
<a id="__codelineno-0-81" name="__codelineno-0-81" href="#__codelineno-0-81"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/orders/"</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">Order</span><span class="p">)</span>
|
|
||||||
<a id="__codelineno-0-82" name="__codelineno-0-82" href="#__codelineno-0-82"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_order</span><span class="p">(</span><span class="n">order</span><span class="p">:</span> <span class="n">Order</span><span class="p">):</span>
|
|
||||||
<a id="__codelineno-0-83" name="__codelineno-0-83" href="#__codelineno-0-83"></a> <span class="n">created</span> <span class="o">=</span> <span class="k">await</span> <span class="n">create_order_with_inventory</span><span class="p">(</span><span class="n">order</span><span class="p">)</span>
|
|
||||||
<a id="__codelineno-0-84" name="__codelineno-0-84" href="#__codelineno-0-84"></a> <span class="k">if</span> <span class="n">created</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
|
||||||
<a id="__codelineno-0-85" name="__codelineno-0-85" href="#__codelineno-0-85"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"Transaction failed"</span><span class="p">)</span>
|
|
||||||
<a id="__codelineno-0-86" name="__codelineno-0-86" href="#__codelineno-0-86"></a> <span class="k">return</span> <span class="n">created</span>
|
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="tips">💡 Tips</h2>
|
<h2 id="tips">💡 Tips</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Return values:</strong> Each callable can return whatever you need (inserted document, stats, etc.). The helper collects them in order.</li>
|
<li><strong>Return values:</strong> each callable can return whatever you need; results are collected in the same order.</li>
|
||||||
<li><strong>Error handling:</strong> Raising any exception inside a callable will abort the transaction automatically.</li>
|
<li><strong>Errors:</strong> raise inside any callable → the whole transaction aborts (session rolls back) and the exception propagates.</li>
|
||||||
<li><strong>Read‑only ops:</strong> You can also run queries inside the transaction – just pass <code>session=session</code> to the <code>find_one</code>/<code>find</code> calls.</li>
|
<li><strong>Reads inside a transaction:</strong> pass <code>session=session</code> to <code>find_one</code>/<code>find</code> too.</li>
|
||||||
<li><strong>Testing:</strong> In unit tests you can mock <code>TransactionManager.start_session</code> to ensure the helpers are called.</li>
|
<li><strong>Testing (no Mongo):</strong> <code>monkeypatch</code> a fake client on <code>mongo_ops.transactions.MongoConnectionManager.get_client</code> and stub <code>start_session</code> — see <code>tests/test_transactions.py</code>.</li>
|
||||||
|
<li>Repo <strong>models</strong> still carry <code>created_at</code>/<code>updated_at</code>; for raw collection inserts inside the transaction you set them manually (as shown).</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../03_transactions/">03 – Transactions</a> · <a href="../14_testing_guide/">14 – Testing guide</a> · <a href="../../06_error_handling/">Error Handling</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
<div data-md-component="skip">
|
<div data-md-component="skip">
|
||||||
|
|
||||||
|
|
||||||
<a href="#use-case-13-declaring-indexes-singlefield-composite-unique" class="md-skip">
|
<a href="#use-case-13-declaring-indexes-single-field-composite-unique" class="md-skip">
|
||||||
Skip to content
|
Skip to content
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -851,7 +851,7 @@
|
|||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#whats-new" class="md-nav__link">
|
<a href="#whats-new" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
📦 What’s New?
|
📦 What's New?
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -873,6 +873,15 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
@@ -1026,11 +1035,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1 id="use-case-13-declaring-indexes-singlefield-composite-unique">Use Case 13: Declaring Indexes (Single‑field, Composite, Unique)</h1>
|
<h1 id="use-case-13-declaring-indexes-single-field-composite-unique">Use Case 13: Declaring Indexes (Single-Field, Composite, Unique)</h1>
|
||||||
<p><strong>Scenario:</strong>
|
<p><strong>Scenario:</strong> Ensure each collection has the right indexes for fast queries and data integrity — declared in one place and created at startup.</p>
|
||||||
You want to ensure your collection has the right indexes for fast queries and data integrity. This doc shows how to register models with different index configurations using <code>ModelRegistry.register</code> and have them created automatically during startup.</p>
|
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="whats-new">📦 What’s New?</h2>
|
<h2 id="whats-new">📦 What's New?</h2>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -1040,87 +1048,93 @@ You want to ensure your collection has the right indexes for fast queries and da
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>ModelRegistry.register</code> (indexes argument)</td>
|
<td><code>ModelRegistry.register(indexes=...)</code></td>
|
||||||
<td>Accepts a list of index specifications. Each spec can be a tuple <code>(field, direction)</code> or a full dict with <code>keys</code> and optional options (e.g., <code>unique</code>).</td>
|
<td>Each spec is passed <strong>as-is</strong> to pymongo <code>create_index</code>. Supported forms: tuple <code>("field", direction)</code>, compound list <code>[("a", 1), ("b", -1)]</code>, or a dict with <code>keys</code> + <code>options</code>.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>ModelRegistry.initialize_all</code></td>
|
<td><code>ModelRegistry.initialize_all</code></td>
|
||||||
<td>Scans all registered collections and creates the defined indexes on application start‑up.</td>
|
<td>Creates every registered index during startup (idempotent — <code>create_index</code> skips existing indexes).</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="example">🚀 Example</h2>
|
<h2 id="example">🚀 Example</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">ModelRegistry</span><span class="p">,</span> <span class="n">BaseDocument</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">pymongo</span><span class="w"> </span><span class="kn">import</span> <span class="n">ASCENDING</span><span class="p">,</span> <span class="n">DESCENDING</span>
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">pymongo</span><span class="w"> </span><span class="kn">import</span> <span class="n">ASCENDING</span><span class="p">,</span> <span class="n">DESCENDING</span>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">ModelRegistry</span><span class="p">,</span> <span class="n">MongoConnectionManager</span>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="c1"># 1️⃣ Simple single‑field index (e.g., email lookup)</span>
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span>
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"users"</span><span class="p">,</span>
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a><span class="k">class</span><span class="w"> </span><span class="nc">BlogPost</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">model</span><span class="o">=</span><span class="n">User</span><span class="p">,</span>
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">author_id</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">indexes</span><span class="o">=</span><span class="p">[(</span><span class="s2">"email"</span><span class="p">,</span> <span class="n">ASCENDING</span><span class="p">)],</span> <span class="c1"># creates an index on "email"</span>
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">created_at</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a><span class="p">)</span>
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a>
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a><span class="c1"># 2️⃣ Composite index (e.g., queries filtering by user and creation date)</span>
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a><span class="k">class</span><span class="w"> </span><span class="nc">Passenger</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a><span class="k">class</span><span class="w"> </span><span class="nc">BlogPost</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> <span class="n">seat</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> <span class="n">author_id</span><span class="p">:</span> <span class="nb">str</span>
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a>
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="n">created_at</span><span class="p">:</span> <span class="nb">str</span>
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a>
|
||||||
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span>
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a><span class="c1"># 1️⃣ Single-field index — email lookups.</span>
|
||||||
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a>
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span>
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"users"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"posts"</span><span class="p">,</span>
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> <span class="n">model</span><span class="o">=</span><span class="n">User</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a> <span class="n">model</span><span class="o">=</span><span class="n">BlogPost</span><span class="p">,</span>
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a> <span class="n">indexes</span><span class="o">=</span><span class="p">[(</span><span class="s2">"email"</span><span class="p">,</span> <span class="n">ASCENDING</span><span class="p">)],</span>
|
||||||
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a> <span class="n">indexes</span><span class="o">=</span><span class="p">[[(</span><span class="s2">"author_id"</span><span class="p">,</span> <span class="n">ASCENDING</span><span class="p">),</span> <span class="p">(</span><span class="s2">"created_at"</span><span class="p">,</span> <span class="n">DESCENDING</span><span class="p">)]],</span> <span class="c1"># compound index</span>
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a><span class="p">)</span>
|
||||||
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a><span class="p">)</span>
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a>
|
||||||
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a>
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a><span class="c1"># 2️⃣ Composite index — queries filtering by author + creation date.</span>
|
||||||
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a><span class="c1"># 3️⃣ Unique index (e.g., enforce unique usernames)</span>
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"posts"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="n">model</span><span class="o">=</span><span class="n">BlogPost</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span>
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="n">indexes</span><span class="o">=</span><span class="p">[[(</span><span class="s2">"author_id"</span><span class="p">,</span> <span class="n">ASCENDING</span><span class="p">),</span> <span class="p">(</span><span class="s2">"created_at"</span><span class="p">,</span> <span class="n">DESCENDING</span><span class="p">)]],</span>
|
||||||
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"users"</span><span class="p">,</span>
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a><span class="p">)</span>
|
||||||
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="n">model</span><span class="o">=</span><span class="n">User</span><span class="p">,</span>
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a>
|
||||||
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="n">indexes</span><span class="o">=</span><span class="p">[</span>
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a><span class="c1"># 3️⃣ Unique index with a custom name — enforce unique emails.</span>
|
||||||
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="p">{</span>
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a><span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> <span class="s2">"keys"</span><span class="p">:</span> <span class="p">[(</span><span class="s2">"username"</span><span class="p">,</span> <span class="n">ASCENDING</span><span class="p">)],</span>
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"passengers"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="s2">"options"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"unique"</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s2">"name"</span><span class="p">:</span> <span class="s2">"uq_username"</span><span class="p">},</span>
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="n">model</span><span class="o">=</span><span class="n">Passenger</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="p">}</span>
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="n">indexes</span><span class="o">=</span><span class="p">[</span>
|
||||||
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="p">],</span>
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="p">{</span>
|
||||||
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a><span class="p">)</span>
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="s2">"keys"</span><span class="p">:</span> <span class="p">[(</span><span class="s2">"email"</span><span class="p">,</span> <span class="n">ASCENDING</span><span class="p">)],</span>
|
||||||
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a>
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="s2">"options"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"unique"</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s2">"name"</span><span class="p">:</span> <span class="s2">"uq_passenger_email"</span><span class="p">},</span>
|
||||||
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="p">}</span>
|
||||||
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a><span class="c1"># 4️⃣ Initialise all indexes during FastAPI startup</span>
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="p">],</span>
|
||||||
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a><span class="p">)</span>
|
||||||
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">MongoConnectionManager</span>
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a>
|
||||||
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a>
|
||||||
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a>
|
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a><span class="c1"># 4️⃣ Everything is created on startup.</span>
|
||||||
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a><span class="nd">@asynccontextmanager</span>
|
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a><span class="nd">@asynccontextmanager</span>
|
||||||
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
|
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">_app</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">lifespan</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span>
|
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="n">db_name</span><span class="o">=</span><span class="s2">"mydb"</span><span class="p">,</span>
|
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="n">db_name</span><span class="o">=</span><span class="s2">"mydb"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a> <span class="p">):</span>
|
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a> <span class="p">):</span>
|
||||||
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a> <span class="c1"># This will create every index declared above (if it does not already exist)</span>
|
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">()</span>
|
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> <span class="k">yield</span>
|
||||||
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> <span class="k">yield</span>
|
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="tips">💡 Tips</h2>
|
<h2 id="tips">💡 Tips</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Idempotence:</strong> <code>initialize_all</code> uses <code>create_index</code>, which is safe to run multiple times – MongoDB will skip creation if the index already exists.</li>
|
<li>Every spec passes through to <code>collection.create_index(spec)</code> — so MongoDB options like <code>unique</code>, <code>sparse</code>, and <code>expireAfterSeconds</code> (TTL) belong in the <code>options</code> dict.</li>
|
||||||
<li><strong>Index options:</strong> You can also set <code>expireAfterSeconds</code> for TTL indexes, <code>sparse</code> for sparse indexes, etc., by adding them to the <code>options</code> dict.</li>
|
<li>Idempotent by construction: <code>create_index</code> is a no-op when a same-shape index already exists.</li>
|
||||||
<li><strong>Verification:</strong> After the app starts, you can verify indexes with the Mongo shell:
|
<li>Verify with the Mongo shell:</li>
|
||||||
<div class="language-js highlight"><span class="filename">JavaScript</span><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="nx">db</span><span class="p">.</span><span class="o"><</span><span class="nx">collection</span><span class="o">></span><span class="p">.</span><span class="nx">getIndexes</span><span class="p">()</span>
|
</ul>
|
||||||
</code></pre></div></li>
|
<div class="language-js highlight"><span class="filename">JavaScript</span><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="nx">db</span><span class="p">.</span><span class="o"><</span><span class="nx">collection</span><span class="o">></span><span class="p">.</span><span class="nx">getIndexes</span><span class="p">()</span>
|
||||||
|
</code></pre></div>
|
||||||
|
<ul>
|
||||||
|
<li>A <strong>unique</strong> index on an already-duplicated field will fail with <code>DuplicateKeyError</code> on startup — clean the data first.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../01_basic_crud/">01 – Basic CRUD</a> · <a href="../06_multi_model/">06 – Multi-model</a> · <a href="../../02_components/">Components: ModelRegistry</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
<div data-md-component="skip">
|
<div data-md-component="skip">
|
||||||
|
|
||||||
|
|
||||||
<a href="#use-case-14-testing-guide-mocking-motor-population-engine" class="md-skip">
|
<a href="#use-case-14-testing-guide-mocking-motor-cache-registry-transactions" class="md-skip">
|
||||||
Skip to content
|
Skip to content
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -874,16 +874,16 @@
|
|||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#whats-new" class="md-nav__link">
|
<a href="#whats-new" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
📦 What’s New?
|
📦 What's New?
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#example-test-boilerplate" class="md-nav__link">
|
<a href="#example-boilerplate" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
🚀 Example Test Boilerplate
|
🚀 Example Boilerplate
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -896,6 +896,15 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
@@ -1026,11 +1035,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1 id="use-case-14-testing-guide-mocking-motor-population-engine">Use Case 14: Testing Guide – Mocking Motor & Population Engine</h1>
|
<h1 id="use-case-14-testing-guide-mocking-motor-cache-registry-transactions">Use Case 14: Testing Guide — Mocking Motor, Cache, Registry & Transactions</h1>
|
||||||
<p><strong>Scenario:</strong>
|
<p><strong>Scenario:</strong> Write unit tests that never touch a real MongoDB — mock the collection, the cache backend, and the client, exactly like the library's own test suite (<code>tests/</code>).</p>
|
||||||
You want to write unit tests for repositories that use async Motor collections and the <code>PopulationEngine</code> without connecting to a real MongoDB instance.</p>
|
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="whats-new">📦 What’s New?</h2>
|
<h2 id="whats-new">📦 What's New?</h2>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -1040,132 +1048,204 @@ You want to write unit tests for repositories that use async Motor collections a
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>AsyncMock</code> for collections</td>
|
<td><code>AsyncMock</code> collections</td>
|
||||||
<td>Allows you to stub <code>find_one</code>, <code>insert_one</code>, etc., and control returned data.</td>
|
<td>Stub <code>find_one</code>, <code>insert_one</code>, <code>find_one_and_update</code>, cursor chains.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Patching <code>MongoConnectionManager.get_database</code></td>
|
<td>Patching <code>MongoConnectionManager.get_database</code></td>
|
||||||
<td>Redirects repository initialization to a mock collection.</td>
|
<td>Gives repositories a mock collection without a live connection.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Mocking <code>PopulationEngine</code></td>
|
<td><code>monkeypatch</code> on <code>get_client</code></td>
|
||||||
<td>Replace the real engine with a lightweight stub that returns pre‑crafted objects.</td>
|
<td>Fakes sessions for <code>TransactionManager</code> tests.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Example test files</td>
|
<td>Reference tests</td>
|
||||||
<td><code>tests/test_populating_repository.py</code> and <code>tests/test_transactions.py</code> are used as reference implementations.</td>
|
<td><code>tests/test_populating_repository.py</code>, <code>tests/test_cache.py</code>, <code>tests/test_registry.py</code>, <code>tests/test_transactions.py</code>.</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="example-test-boilerplate">🚀 Example Test Boilerplate</h2>
|
<h2 id="example-boilerplate">🚀 Example Boilerplate</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">import</span><span class="w"> </span><span class="nn">pytest</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">import</span><span class="w"> </span><span class="nn">pytest</span>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">unittest.mock</span><span class="w"> </span><span class="kn">import</span> <span class="n">AsyncMock</span><span class="p">,</span> <span class="n">patch</span>
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">unittest.mock</span><span class="w"> </span><span class="kn">import</span> <span class="n">AsyncMock</span><span class="p">,</span> <span class="n">MagicMock</span><span class="p">,</span> <span class="n">patch</span>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a>
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.models</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span>
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.cache</span><span class="w"> </span><span class="kn">import</span> <span class="n">CacheConfig</span><span class="p">,</span> <span class="n">InMemoryCacheBackend</span><span class="p">,</span> <span class="n">CachedBaseRepository</span>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.populate</span><span class="w"> </span><span class="kn">import</span> <span class="n">PopulateRule</span><span class="p">,</span> <span class="n">PopulationEngine</span>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.models</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.repository</span><span class="w"> </span><span class="kn">import</span> <span class="n">PopulatingRepository</span>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.registry</span><span class="w"> </span><span class="kn">import</span> <span class="n">ModelRegistry</span>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.populate</span><span class="w"> </span><span class="kn">import</span> <span class="n">PopulateRule</span><span class="p">,</span> <span class="n">PopulationEngine</span>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.repository</span><span class="w"> </span><span class="kn">import</span> <span class="n">PopulatingRepository</span>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a><span class="c1"># 1️⃣ Define simple models (same as in the library tests)</span>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a><span class="k">class</span><span class="w"> </span><span class="nc">Profile</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">avatar_url</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a><span class="c1"># 1. Models (same shape as the library tests)</span>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a>
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a><span class="k">class</span><span class="w"> </span><span class="nc">Profile</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="n">avatar_url</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">profile_id</span><span class="p">:</span> <span class="n">ObjectId</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="n">profile</span><span class="p">:</span> <span class="n">Profile</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a>
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="c1"># 2️⃣ Fixture – mock Motor collection</span>
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> <span class="n">profile</span><span class="p">:</span> <span class="n">Profile</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># ObjectId in DB, Profile in memory</span>
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a>
|
||||||
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a>
|
||||||
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a><span class="k">def</span><span class="w"> </span><span class="nf">mock_collection</span><span class="p">():</span>
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
||||||
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="k">return</span> <span class="n">AsyncMock</span><span class="p">()</span>
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a><span class="c1"># 2. Fixtures</span>
|
||||||
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a>
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
||||||
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
|
||||||
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a><span class="c1"># 3️⃣ Fixture – mock PopulationEngine (optional)</span>
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a><span class="k">def</span><span class="w"> </span><span class="nf">mock_collection</span><span class="p">():</span>
|
||||||
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a> <span class="k">return</span> <span class="n">AsyncMock</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a>
|
||||||
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a><span class="k">def</span><span class="w"> </span><span class="nf">engine</span><span class="p">():</span>
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a>
|
||||||
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="c1"># Provide a real engine but replace the repo for "profiles" with a mock</span>
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
|
||||||
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="n">profile_repo</span> <span class="o">=</span> <span class="n">AsyncMock</span><span class="p">()</span>
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a><span class="k">def</span><span class="w"> </span><span class="nf">engine</span><span class="p">():</span>
|
||||||
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="k">return</span> <span class="n">PopulationEngine</span><span class="p">({</span><span class="s2">"profiles"</span><span class="p">:</span> <span class="n">profile_repo</span><span class="p">})</span>
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="n">profile_repo</span> <span class="o">=</span> <span class="n">AsyncMock</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a>
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="k">return</span> <span class="n">PopulationEngine</span><span class="p">({</span><span class="s2">"profiles"</span><span class="p">:</span> <span class="n">profile_repo</span><span class="p">})</span>
|
||||||
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a>
|
||||||
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a><span class="c1"># 4️⃣ Fixture – repository under test</span>
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a>
|
||||||
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
|
||||||
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a><span class="k">def</span><span class="w"> </span><span class="nf">repo</span><span class="p">(</span><span class="n">mock_collection</span><span class="p">,</span> <span class="n">engine</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a><span class="k">def</span><span class="w"> </span><span class="nf">repo</span><span class="p">(</span><span class="n">mock_collection</span><span class="p">,</span> <span class="n">engine</span><span class="p">):</span>
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> <span class="k">with</span> <span class="n">patch</span><span class="p">(</span><span class="s2">"mongo_ops.repository.MongoConnectionManager.get_database"</span><span class="p">)</span> <span class="k">as</span> <span class="n">mock_db</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="k">with</span> <span class="n">patch</span><span class="p">(</span><span class="s2">"mongo_ops.repository.MongoConnectionManager.get_database"</span><span class="p">)</span> <span class="k">as</span> <span class="n">mock_db</span><span class="p">:</span>
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="n">mock_db</span><span class="o">.</span><span class="n">return_value</span><span class="o">.</span><span class="fm">__getitem__</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="n">mock_collection</span>
|
||||||
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="n">mock_db</span><span class="o">.</span><span class="n">return_value</span><span class="o">.</span><span class="fm">__getitem__</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="n">mock_collection</span>
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="n">r</span> <span class="o">=</span> <span class="n">PopulatingRepository</span><span class="p">(</span>
|
||||||
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="n">repo</span> <span class="o">=</span> <span class="n">PopulatingRepository</span><span class="p">(</span>
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="s2">"users"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="s2">"users"</span><span class="p">,</span>
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="n">User</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="n">User</span><span class="p">,</span>
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="n">population_engine</span><span class="o">=</span><span class="n">engine</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="n">population_engine</span><span class="o">=</span><span class="n">engine</span><span class="p">,</span>
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="n">populate_rules</span><span class="o">=</span><span class="p">[</span><span class="n">PopulateRule</span><span class="p">(</span><span class="n">field_name</span><span class="o">=</span><span class="s2">"profile"</span><span class="p">,</span> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"profiles"</span><span class="p">)],</span>
|
||||||
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="n">populate_rules</span><span class="o">=</span><span class="p">[</span>
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="p">)</span>
|
||||||
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="n">PopulateRule</span><span class="p">(</span>
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="n">r</span><span class="o">.</span><span class="n">collection</span> <span class="o">=</span> <span class="n">mock_collection</span>
|
||||||
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="n">field_name</span><span class="o">=</span><span class="s2">"profile"</span><span class="p">,</span>
|
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="k">return</span> <span class="n">r</span>
|
||||||
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">"profiles"</span><span class="p">,</span>
|
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a>
|
||||||
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a> <span class="n">ref_field</span><span class="o">=</span><span class="s2">"profile_id"</span><span class="p">,</span>
|
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a>
|
||||||
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a> <span class="p">)</span>
|
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
||||||
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a> <span class="p">],</span>
|
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a><span class="c1"># 3. Population — get_by_id resolves the reference</span>
|
||||||
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="p">)</span>
|
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
||||||
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="n">repo</span><span class="o">.</span><span class="n">collection</span> <span class="o">=</span> <span class="n">mock_collection</span>
|
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">asyncio</span>
|
||||||
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a> <span class="k">return</span> <span class="n">repo</span>
|
<a id="__codelineno-0-56" name="__codelineno-0-56" href="#__codelineno-0-56"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">test_get_by_id_populates</span><span class="p">(</span><span class="n">repo</span><span class="p">,</span> <span class="n">mock_collection</span><span class="p">,</span> <span class="n">engine</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a>
|
<a id="__codelineno-0-57" name="__codelineno-0-57" href="#__codelineno-0-57"></a> <span class="n">uid</span><span class="p">,</span> <span class="n">pid</span> <span class="o">=</span> <span class="n">ObjectId</span><span class="p">(),</span> <span class="n">ObjectId</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-58" name="__codelineno-0-58" href="#__codelineno-0-58"></a> <span class="n">mock_collection</span><span class="o">.</span><span class="n">find_one</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="p">{</span>
|
||||||
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a><span class="c1"># 5️⃣ Test – `get_by_id` populates the related document</span>
|
<a id="__codelineno-0-59" name="__codelineno-0-59" href="#__codelineno-0-59"></a> <span class="s2">"_id"</span><span class="p">:</span> <span class="n">uid</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-60" name="__codelineno-0-60" href="#__codelineno-0-60"></a> <span class="s2">"name"</span><span class="p">:</span> <span class="s2">"Alice"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">asyncio</span>
|
<a id="__codelineno-0-61" name="__codelineno-0-61" href="#__codelineno-0-61"></a> <span class="s2">"profile"</span><span class="p">:</span> <span class="n">pid</span><span class="p">,</span> <span class="c1"># ObjectId stored in DB</span>
|
||||||
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">test_get_by_id_populates</span><span class="p">(</span><span class="n">repo</span><span class="p">,</span> <span class="n">mock_collection</span><span class="p">,</span> <span class="n">engine</span><span class="p">):</span>
|
<a id="__codelineno-0-62" name="__codelineno-0-62" href="#__codelineno-0-62"></a> <span class="s2">"created_at"</span><span class="p">:</span> <span class="s2">"2024-01-01T00:00:00"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a> <span class="c1"># Arrange – user document returned from the DB</span>
|
<a id="__codelineno-0-63" name="__codelineno-0-63" href="#__codelineno-0-63"></a> <span class="s2">"updated_at"</span><span class="p">:</span> <span class="s2">"2024-01-01T00:00:00"</span><span class="p">,</span>
|
||||||
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a> <span class="n">uid</span> <span class="o">=</span> <span class="n">ObjectId</span><span class="p">()</span>
|
<a id="__codelineno-0-64" name="__codelineno-0-64" href="#__codelineno-0-64"></a> <span class="p">}</span>
|
||||||
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a> <span class="n">pid</span> <span class="o">=</span> <span class="n">ObjectId</span><span class="p">()</span>
|
<a id="__codelineno-0-65" name="__codelineno-0-65" href="#__codelineno-0-65"></a> <span class="n">engine</span><span class="o">.</span><span class="n">_repos</span><span class="p">[</span><span class="s2">"profiles"</span><span class="p">]</span><span class="o">.</span><span class="n">get_by_id</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="n">Profile</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">pid</span><span class="p">,</span> <span class="n">avatar_url</span><span class="o">=</span><span class="s2">"pic.png"</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a> <span class="n">mock_collection</span><span class="o">.</span><span class="n">find_one</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="p">{</span>
|
<a id="__codelineno-0-66" name="__codelineno-0-66" href="#__codelineno-0-66"></a>
|
||||||
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a> <span class="s2">"_id"</span><span class="p">:</span> <span class="n">uid</span><span class="p">,</span>
|
<a id="__codelineno-0-67" name="__codelineno-0-67" href="#__codelineno-0-67"></a> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="n">uid</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a> <span class="s2">"name"</span><span class="p">:</span> <span class="s2">"Alice"</span><span class="p">,</span>
|
<a id="__codelineno-0-68" name="__codelineno-0-68" href="#__codelineno-0-68"></a>
|
||||||
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a> <span class="s2">"profile_id"</span><span class="p">:</span> <span class="n">pid</span><span class="p">,</span>
|
<a id="__codelineno-0-69" name="__codelineno-0-69" href="#__codelineno-0-69"></a> <span class="k">assert</span> <span class="n">result</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
|
||||||
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a> <span class="s2">"created_at"</span><span class="p">:</span> <span class="s2">"2024-01-01T00:00:00"</span><span class="p">,</span>
|
<a id="__codelineno-0-70" name="__codelineno-0-70" href="#__codelineno-0-70"></a> <span class="k">assert</span> <span class="n">result</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">"Alice"</span>
|
||||||
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a> <span class="s2">"updated_at"</span><span class="p">:</span> <span class="s2">"2024-01-01T00:00:00"</span><span class="p">,</span>
|
<a id="__codelineno-0-71" name="__codelineno-0-71" href="#__codelineno-0-71"></a> <span class="k">assert</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">result</span><span class="o">.</span><span class="n">profile</span><span class="p">,</span> <span class="n">Profile</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a> <span class="p">}</span>
|
<a id="__codelineno-0-72" name="__codelineno-0-72" href="#__codelineno-0-72"></a> <span class="k">assert</span> <span class="n">result</span><span class="o">.</span><span class="n">profile</span><span class="o">.</span><span class="n">avatar_url</span> <span class="o">==</span> <span class="s2">"pic.png"</span>
|
||||||
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a> <span class="c1"># Mock the profile repo to return a concrete Profile instance</span>
|
<a id="__codelineno-0-73" name="__codelineno-0-73" href="#__codelineno-0-73"></a>
|
||||||
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a> <span class="n">engine</span><span class="o">.</span><span class="n">_repos</span><span class="p">[</span><span class="s2">"profiles"</span><span class="p">]</span><span class="o">.</span><span class="n">get_by_id</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="n">Profile</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">pid</span><span class="p">,</span> <span class="n">avatar_url</span><span class="o">=</span><span class="s2">"pic.png"</span><span class="p">)</span>
|
<a id="__codelineno-0-74" name="__codelineno-0-74" href="#__codelineno-0-74"></a>
|
||||||
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a>
|
<a id="__codelineno-0-75" name="__codelineno-0-75" href="#__codelineno-0-75"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
||||||
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a> <span class="c1"># Act</span>
|
<a id="__codelineno-0-76" name="__codelineno-0-76" href="#__codelineno-0-76"></a><span class="c1"># 4. Patch FK guard</span>
|
||||||
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="n">uid</span><span class="p">)</span>
|
<a id="__codelineno-0-77" name="__codelineno-0-77" href="#__codelineno-0-77"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
||||||
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a>
|
<a id="__codelineno-0-78" name="__codelineno-0-78" href="#__codelineno-0-78"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">asyncio</span>
|
||||||
<a id="__codelineno-0-79" name="__codelineno-0-79" href="#__codelineno-0-79"></a> <span class="c1"># Assert</span>
|
<a id="__codelineno-0-79" name="__codelineno-0-79" href="#__codelineno-0-79"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">test_patch_rejects_fk_field</span><span class="p">(</span><span class="n">repo</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-80" name="__codelineno-0-80" href="#__codelineno-0-80"></a> <span class="k">assert</span> <span class="n">result</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
|
<a id="__codelineno-0-80" name="__codelineno-0-80" href="#__codelineno-0-80"></a> <span class="k">with</span> <span class="n">pytest</span><span class="o">.</span><span class="n">raises</span><span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="n">match</span><span class="o">=</span><span class="s2">"Cannot patch FK fields"</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-81" name="__codelineno-0-81" href="#__codelineno-0-81"></a> <span class="k">assert</span> <span class="n">result</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">"Alice"</span>
|
<a id="__codelineno-0-81" name="__codelineno-0-81" href="#__codelineno-0-81"></a> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">patch</span><span class="p">(</span><span class="n">ObjectId</span><span class="p">(),</span> <span class="p">{</span><span class="s2">"profile"</span><span class="p">:</span> <span class="n">ObjectId</span><span class="p">()})</span>
|
||||||
<a id="__codelineno-0-82" name="__codelineno-0-82" href="#__codelineno-0-82"></a> <span class="k">assert</span> <span class="n">result</span><span class="o">.</span><span class="n">profile</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
|
<a id="__codelineno-0-82" name="__codelineno-0-82" href="#__codelineno-0-82"></a>
|
||||||
<a id="__codelineno-0-83" name="__codelineno-0-83" href="#__codelineno-0-83"></a> <span class="k">assert</span> <span class="n">result</span><span class="o">.</span><span class="n">profile</span><span class="o">.</span><span class="n">avatar_url</span> <span class="o">==</span> <span class="s2">"pic.png"</span>
|
<a id="__codelineno-0-83" name="__codelineno-0-83" href="#__codelineno-0-83"></a>
|
||||||
<a id="__codelineno-0-84" name="__codelineno-0-84" href="#__codelineno-0-84"></a>
|
<a id="__codelineno-0-84" name="__codelineno-0-84" href="#__codelineno-0-84"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
||||||
<a id="__codelineno-0-85" name="__codelineno-0-85" href="#__codelineno-0-85"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-85" name="__codelineno-0-85" href="#__codelineno-0-85"></a><span class="c1"># 5. Registry — index specs pass through to create_index</span>
|
||||||
<a id="__codelineno-0-86" name="__codelineno-0-86" href="#__codelineno-0-86"></a><span class="c1"># 6️⃣ Test – transaction helper uses the real `TransactionManager`</span>
|
<a id="__codelineno-0-86" name="__codelineno-0-86" href="#__codelineno-0-86"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
||||||
<a id="__codelineno-0-87" name="__codelineno-0-87" href="#__codelineno-0-87"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
<a id="__codelineno-0-87" name="__codelineno-0-87" href="#__codelineno-0-87"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">asyncio</span>
|
||||||
<a id="__codelineno-0-88" name="__codelineno-0-88" href="#__codelineno-0-88"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.transactions</span><span class="w"> </span><span class="kn">import</span> <span class="n">TransactionManager</span>
|
<a id="__codelineno-0-88" name="__codelineno-0-88" href="#__codelineno-0-88"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">test_initialize_all_creates_indexes</span><span class="p">():</span>
|
||||||
<a id="__codelineno-0-89" name="__codelineno-0-89" href="#__codelineno-0-89"></a>
|
<a id="__codelineno-0-89" name="__codelineno-0-89" href="#__codelineno-0-89"></a> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">User</span><span class="p">,</span> <span class="n">indexes</span><span class="o">=</span><span class="p">[(</span><span class="s2">"email"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)])</span>
|
||||||
<a id="__codelineno-0-90" name="__codelineno-0-90" href="#__codelineno-0-90"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">asyncio</span>
|
<a id="__codelineno-0-90" name="__codelineno-0-90" href="#__codelineno-0-90"></a>
|
||||||
<a id="__codelineno-0-91" name="__codelineno-0-91" href="#__codelineno-0-91"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">test_execute_transaction</span><span class="p">(</span><span class="n">monkeypatch</span><span class="p">):</span>
|
<a id="__codelineno-0-91" name="__codelineno-0-91" href="#__codelineno-0-91"></a> <span class="n">fake_collection</span> <span class="o">=</span> <span class="n">AsyncMock</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-92" name="__codelineno-0-92" href="#__codelineno-0-92"></a> <span class="c1"># Fake a Motor client session that records calls</span>
|
<a id="__codelineno-0-92" name="__codelineno-0-92" href="#__codelineno-0-92"></a> <span class="k">await</span> <span class="n">ModelRegistry</span><span class="o">.</span><span class="n">initialize_all</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="p">{</span><span class="s2">"users"</span><span class="p">:</span> <span class="n">fake_collection</span><span class="p">})</span>
|
||||||
<a id="__codelineno-0-93" name="__codelineno-0-93" href="#__codelineno-0-93"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">fake_op</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
<a id="__codelineno-0-93" name="__codelineno-0-93" href="#__codelineno-0-93"></a>
|
||||||
<a id="__codelineno-0-94" name="__codelineno-0-94" href="#__codelineno-0-94"></a> <span class="k">return</span> <span class="s2">"ok"</span>
|
<a id="__codelineno-0-94" name="__codelineno-0-94" href="#__codelineno-0-94"></a> <span class="n">fake_collection</span><span class="o">.</span><span class="n">create_index</span><span class="o">.</span><span class="n">assert_awaited_once_with</span><span class="p">((</span><span class="s2">"email"</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span>
|
||||||
<a id="__codelineno-0-95" name="__codelineno-0-95" href="#__codelineno-0-95"></a> <span class="c1"># Execute with the helper – it should return a list with the result</span>
|
<a id="__codelineno-0-95" name="__codelineno-0-95" href="#__codelineno-0-95"></a>
|
||||||
<a id="__codelineno-0-96" name="__codelineno-0-96" href="#__codelineno-0-96"></a> <span class="n">results</span> <span class="o">=</span> <span class="k">await</span> <span class="n">TransactionManager</span><span class="o">.</span><span class="n">execute_transaction</span><span class="p">([</span><span class="n">fake_op</span><span class="p">])</span>
|
<a id="__codelineno-0-96" name="__codelineno-0-96" href="#__codelineno-0-96"></a>
|
||||||
<a id="__codelineno-0-97" name="__codelineno-0-97" href="#__codelineno-0-97"></a> <span class="k">assert</span> <span class="n">results</span> <span class="o">==</span> <span class="p">[</span><span class="s2">"ok"</span><span class="p">]</span>
|
<a id="__codelineno-0-97" name="__codelineno-0-97" href="#__codelineno-0-97"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
||||||
|
<a id="__codelineno-0-98" name="__codelineno-0-98" href="#__codelineno-0-98"></a><span class="c1"># 6. Cached repository — cache-first reads + invalidation</span>
|
||||||
|
<a id="__codelineno-0-99" name="__codelineno-0-99" href="#__codelineno-0-99"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
||||||
|
<a id="__codelineno-0-100" name="__codelineno-0-100" href="#__codelineno-0-100"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">asyncio</span>
|
||||||
|
<a id="__codelineno-0-101" name="__codelineno-0-101" href="#__codelineno-0-101"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">test_cached_get_by_id_populates_cache</span><span class="p">():</span>
|
||||||
|
<a id="__codelineno-0-102" name="__codelineno-0-102" href="#__codelineno-0-102"></a> <span class="n">backend</span> <span class="o">=</span> <span class="n">InMemoryCacheBackend</span><span class="p">(</span>
|
||||||
|
<a id="__codelineno-0-103" name="__codelineno-0-103" href="#__codelineno-0-103"></a> <span class="n">max_entries</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="n">default_ttl</span><span class="o">=</span><span class="mi">300</span><span class="p">,</span> <span class="n">cleanup_interval</span><span class="o">=</span><span class="mi">9999</span>
|
||||||
|
<a id="__codelineno-0-104" name="__codelineno-0-104" href="#__codelineno-0-104"></a> <span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-105" name="__codelineno-0-105" href="#__codelineno-0-105"></a> <span class="k">await</span> <span class="n">backend</span><span class="o">.</span><span class="n">initialize</span><span class="p">()</span>
|
||||||
|
<a id="__codelineno-0-106" name="__codelineno-0-106" href="#__codelineno-0-106"></a> <span class="k">try</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-107" name="__codelineno-0-107" href="#__codelineno-0-107"></a> <span class="k">with</span> <span class="n">patch</span><span class="p">(</span><span class="s2">"mongo_ops.repository.MongoConnectionManager.get_database"</span><span class="p">)</span> <span class="k">as</span> <span class="n">mock_db</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-108" name="__codelineno-0-108" href="#__codelineno-0-108"></a> <span class="n">mock_collection</span> <span class="o">=</span> <span class="n">AsyncMock</span><span class="p">()</span>
|
||||||
|
<a id="__codelineno-0-109" name="__codelineno-0-109" href="#__codelineno-0-109"></a> <span class="n">mock_db</span><span class="o">.</span><span class="n">return_value</span><span class="o">.</span><span class="fm">__getitem__</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="n">mock_collection</span>
|
||||||
|
<a id="__codelineno-0-110" name="__codelineno-0-110" href="#__codelineno-0-110"></a> <span class="n">repo</span> <span class="o">=</span> <span class="n">CachedBaseRepository</span><span class="p">(</span>
|
||||||
|
<a id="__codelineno-0-111" name="__codelineno-0-111" href="#__codelineno-0-111"></a> <span class="s2">"users"</span><span class="p">,</span> <span class="n">User</span><span class="p">,</span> <span class="n">backend</span><span class="p">,</span> <span class="n">CacheConfig</span><span class="p">(</span><span class="n">enabled</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-112" name="__codelineno-0-112" href="#__codelineno-0-112"></a> <span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-113" name="__codelineno-0-113" href="#__codelineno-0-113"></a> <span class="n">repo</span><span class="o">.</span><span class="n">collection</span> <span class="o">=</span> <span class="n">mock_collection</span>
|
||||||
|
<a id="__codelineno-0-114" name="__codelineno-0-114" href="#__codelineno-0-114"></a>
|
||||||
|
<a id="__codelineno-0-115" name="__codelineno-0-115" href="#__codelineno-0-115"></a> <span class="n">oid</span> <span class="o">=</span> <span class="n">ObjectId</span><span class="p">()</span>
|
||||||
|
<a id="__codelineno-0-116" name="__codelineno-0-116" href="#__codelineno-0-116"></a> <span class="n">mock_collection</span><span class="o">.</span><span class="n">find_one</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="p">{</span>
|
||||||
|
<a id="__codelineno-0-117" name="__codelineno-0-117" href="#__codelineno-0-117"></a> <span class="s2">"_id"</span><span class="p">:</span> <span class="n">oid</span><span class="p">,</span>
|
||||||
|
<a id="__codelineno-0-118" name="__codelineno-0-118" href="#__codelineno-0-118"></a> <span class="s2">"name"</span><span class="p">:</span> <span class="s2">"cached"</span><span class="p">,</span>
|
||||||
|
<a id="__codelineno-0-119" name="__codelineno-0-119" href="#__codelineno-0-119"></a> <span class="s2">"created_at"</span><span class="p">:</span> <span class="s2">"2024-01-01T00:00:00"</span><span class="p">,</span>
|
||||||
|
<a id="__codelineno-0-120" name="__codelineno-0-120" href="#__codelineno-0-120"></a> <span class="s2">"updated_at"</span><span class="p">:</span> <span class="s2">"2024-01-01T00:00:00"</span><span class="p">,</span>
|
||||||
|
<a id="__codelineno-0-121" name="__codelineno-0-121" href="#__codelineno-0-121"></a> <span class="p">}</span>
|
||||||
|
<a id="__codelineno-0-122" name="__codelineno-0-122" href="#__codelineno-0-122"></a>
|
||||||
|
<a id="__codelineno-0-123" name="__codelineno-0-123" href="#__codelineno-0-123"></a> <span class="n">first</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="n">oid</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-124" name="__codelineno-0-124" href="#__codelineno-0-124"></a> <span class="k">assert</span> <span class="n">first</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
|
||||||
|
<a id="__codelineno-0-125" name="__codelineno-0-125" href="#__codelineno-0-125"></a>
|
||||||
|
<a id="__codelineno-0-126" name="__codelineno-0-126" href="#__codelineno-0-126"></a> <span class="n">mock_collection</span><span class="o">.</span><span class="n">find_one</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># DB now "empty"</span>
|
||||||
|
<a id="__codelineno-0-127" name="__codelineno-0-127" href="#__codelineno-0-127"></a> <span class="n">second</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="n">oid</span><span class="p">)</span> <span class="c1"># served from cache</span>
|
||||||
|
<a id="__codelineno-0-128" name="__codelineno-0-128" href="#__codelineno-0-128"></a>
|
||||||
|
<a id="__codelineno-0-129" name="__codelineno-0-129" href="#__codelineno-0-129"></a> <span class="k">assert</span> <span class="n">second</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
|
||||||
|
<a id="__codelineno-0-130" name="__codelineno-0-130" href="#__codelineno-0-130"></a> <span class="k">assert</span> <span class="n">second</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">"cached"</span>
|
||||||
|
<a id="__codelineno-0-131" name="__codelineno-0-131" href="#__codelineno-0-131"></a> <span class="n">mock_collection</span><span class="o">.</span><span class="n">find_one</span><span class="o">.</span><span class="n">assert_awaited_once</span><span class="p">()</span> <span class="c1"># only one DB read</span>
|
||||||
|
<a id="__codelineno-0-132" name="__codelineno-0-132" href="#__codelineno-0-132"></a> <span class="k">finally</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-133" name="__codelineno-0-133" href="#__codelineno-0-133"></a> <span class="k">await</span> <span class="n">backend</span><span class="o">.</span><span class="n">shutdown</span><span class="p">()</span>
|
||||||
|
<a id="__codelineno-0-134" name="__codelineno-0-134" href="#__codelineno-0-134"></a>
|
||||||
|
<a id="__codelineno-0-135" name="__codelineno-0-135" href="#__codelineno-0-135"></a>
|
||||||
|
<a id="__codelineno-0-136" name="__codelineno-0-136" href="#__codelineno-0-136"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
||||||
|
<a id="__codelineno-0-137" name="__codelineno-0-137" href="#__codelineno-0-137"></a><span class="c1"># 7. Transactions — fake the client's start_session</span>
|
||||||
|
<a id="__codelineno-0-138" name="__codelineno-0-138" href="#__codelineno-0-138"></a><span class="c1"># ----------------------------------------------------------------------</span>
|
||||||
|
<a id="__codelineno-0-139" name="__codelineno-0-139" href="#__codelineno-0-139"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.transactions</span><span class="w"> </span><span class="kn">import</span> <span class="n">TransactionManager</span>
|
||||||
|
<a id="__codelineno-0-140" name="__codelineno-0-140" href="#__codelineno-0-140"></a>
|
||||||
|
<a id="__codelineno-0-141" name="__codelineno-0-141" href="#__codelineno-0-141"></a>
|
||||||
|
<a id="__codelineno-0-142" name="__codelineno-0-142" href="#__codelineno-0-142"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">asyncio</span>
|
||||||
|
<a id="__codelineno-0-143" name="__codelineno-0-143" href="#__codelineno-0-143"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">test_execute_transaction</span><span class="p">(</span><span class="n">monkeypatch</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-144" name="__codelineno-0-144" href="#__codelineno-0-144"></a> <span class="n">session_ctx</span> <span class="o">=</span> <span class="n">AsyncMock</span><span class="p">()</span>
|
||||||
|
<a id="__codelineno-0-145" name="__codelineno-0-145" href="#__codelineno-0-145"></a> <span class="n">session_ctx</span><span class="o">.</span><span class="fm">__aenter__</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="n">AsyncMock</span><span class="p">()</span>
|
||||||
|
<a id="__codelineno-0-146" name="__codelineno-0-146" href="#__codelineno-0-146"></a> <span class="n">session_ctx</span><span class="o">.</span><span class="fm">__aexit__</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="kc">None</span>
|
||||||
|
<a id="__codelineno-0-147" name="__codelineno-0-147" href="#__codelineno-0-147"></a>
|
||||||
|
<a id="__codelineno-0-148" name="__codelineno-0-148" href="#__codelineno-0-148"></a> <span class="n">client</span> <span class="o">=</span> <span class="n">MagicMock</span><span class="p">()</span>
|
||||||
|
<a id="__codelineno-0-149" name="__codelineno-0-149" href="#__codelineno-0-149"></a> <span class="n">client</span><span class="o">.</span><span class="n">start_session</span> <span class="o">=</span> <span class="n">AsyncMock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="n">session_ctx</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-150" name="__codelineno-0-150" href="#__codelineno-0-150"></a>
|
||||||
|
<a id="__codelineno-0-151" name="__codelineno-0-151" href="#__codelineno-0-151"></a> <span class="c1"># NOTE: start_transaction must return a context manager, not a coroutine.</span>
|
||||||
|
<a id="__codelineno-0-152" name="__codelineno-0-152" href="#__codelineno-0-152"></a> <span class="n">async_session</span> <span class="o">=</span> <span class="n">session_ctx</span><span class="o">.</span><span class="fm">__aenter__</span><span class="o">.</span><span class="n">return_value</span>
|
||||||
|
<a id="__codelineno-0-153" name="__codelineno-0-153" href="#__codelineno-0-153"></a> <span class="n">async_session</span><span class="o">.</span><span class="n">start_transaction</span> <span class="o">=</span> <span class="k">lambda</span> <span class="o">**</span><span class="n">_</span><span class="p">:</span> <span class="n">session_ctx</span>
|
||||||
|
<a id="__codelineno-0-154" name="__codelineno-0-154" href="#__codelineno-0-154"></a>
|
||||||
|
<a id="__codelineno-0-155" name="__codelineno-0-155" href="#__codelineno-0-155"></a> <span class="n">monkeypatch</span><span class="o">.</span><span class="n">setattr</span><span class="p">(</span>
|
||||||
|
<a id="__codelineno-0-156" name="__codelineno-0-156" href="#__codelineno-0-156"></a> <span class="s2">"mongo_ops.transactions.MongoConnectionManager.get_client"</span><span class="p">,</span>
|
||||||
|
<a id="__codelineno-0-157" name="__codelineno-0-157" href="#__codelineno-0-157"></a> <span class="k">lambda</span><span class="p">:</span> <span class="n">client</span><span class="p">,</span>
|
||||||
|
<a id="__codelineno-0-158" name="__codelineno-0-158" href="#__codelineno-0-158"></a> <span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-159" name="__codelineno-0-159" href="#__codelineno-0-159"></a>
|
||||||
|
<a id="__codelineno-0-160" name="__codelineno-0-160" href="#__codelineno-0-160"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">fake_op</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-161" name="__codelineno-0-161" href="#__codelineno-0-161"></a> <span class="k">return</span> <span class="s2">"ok"</span>
|
||||||
|
<a id="__codelineno-0-162" name="__codelineno-0-162" href="#__codelineno-0-162"></a>
|
||||||
|
<a id="__codelineno-0-163" name="__codelineno-0-163" href="#__codelineno-0-163"></a> <span class="n">results</span> <span class="o">=</span> <span class="k">await</span> <span class="n">TransactionManager</span><span class="o">.</span><span class="n">execute_transaction</span><span class="p">([</span><span class="n">fake_op</span><span class="p">])</span>
|
||||||
|
<a id="__codelineno-0-164" name="__codelineno-0-164" href="#__codelineno-0-164"></a> <span class="k">assert</span> <span class="n">results</span> <span class="o">==</span> <span class="p">[</span><span class="s2">"ok"</span><span class="p">]</span>
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="tips">💡 Tips</h2>
|
<h2 id="tips">💡 Tips</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Never hit the network:</strong> All DB calls are mocked, so tests run instantly.</li>
|
<li>pytest-asyncio is already configured in <code>pyproject.toml</code> (<code>asyncio_mode = "auto"</code>), so <code>@pytest.mark.asyncio</code> tests work out of the box. Run with <code>pytest</code> (coverage reports are enabled there too).</li>
|
||||||
<li><strong>Reuse fixtures:</strong> Keep the <code>mock_collection</code> and <code>engine</code> fixtures in a <code>conftest.py</code> file for other repo tests.</li>
|
<li><strong>Never hit the network.</strong> Keep the patches in fixtures (or a <code>conftest.py</code>) and reuse them.</li>
|
||||||
<li><strong>Coverage:</strong> The same pattern works for <code>CachedBaseRepository</code> – just mock <code>cache_backend</code> methods (<code>get</code>, <code>set</code>, <code>delete</code>).</li>
|
<li>Mock <code>cursor</code> chains with <code>MagicMock()</code> + <code>.to_list = AsyncMock(...)</code>, exactly like <code>tests/test_repository.py</code>.</li>
|
||||||
<li><strong>AsyncTestCase:</strong> If you prefer <code>unittest</code> style, use <code>IsolatedAsyncioTestCase</code> from Python 3.8+.</li>
|
<li>For an optional integration check (real Mongo), use <code>MongoConnectionManager.lifespan</code> against a local replica set and drop the test database in teardown — keep it separate from the unit suite.</li>
|
||||||
|
<li>The pattern works symmetrically for Redis: mock the <code>RedisCacheBackend</code> methods (<code>get</code>, <code>set</code>, <code>delete</code>) — no Redis process required.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../08_population/">08 – Population</a> · <a href="../07_caching/">07 – Caching</a> · <a href="../12_transaction_helper/">12 – Transaction helper</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -454,7 +454,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -861,6 +861,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||||
|
|
||||||
|
|
||||||
@@ -893,6 +895,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__title" for="__toc">
|
<label class="md-nav__title" for="__toc">
|
||||||
<span class="md-nav__icon md-icon"></span>
|
<span class="md-nav__icon md-icon"></span>
|
||||||
Table of contents
|
Table of contents
|
||||||
@@ -900,9 +904,54 @@
|
|||||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#best-practices" class="md-nav__link">
|
<a href="#layering" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Best Practices
|
🏗️ Layering
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#lifecycle" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
🔄 Lifecycle
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#data-performance" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
🗄️ Data & Performance
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#transactions-errors" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
🔁 Transactions & Errors
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#testing" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
🧪 Testing
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -998,20 +1047,46 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1>Best Practices</h1>
|
<h1 id="best-practices">Best Practices</h1>
|
||||||
|
<p>Team-wide conventions for building fast, testable MongoDB services with <code>mongo-ops</code>.</p>
|
||||||
<h2 id="best-practices">Best Practices</h2>
|
<hr />
|
||||||
|
<h2 id="layering">🏗️ Layering</h2>
|
||||||
<ol>
|
<ol>
|
||||||
<li><strong>Always use ModelRegistry.register()</strong> before initializing the database connection</li>
|
<li><strong>One repository per collection.</strong> Encapsulate every query the domain needs behind repository methods; keep Mongo details (<code>$regex</code>, <code>$inc</code>, projections) inside the repository.</li>
|
||||||
<li><strong>Use lifespan context manager</strong> for proper connection lifecycle</li>
|
<li><strong>Keep models thin.</strong> <code>BaseDocument</code> for the shape; use Pydantic <code>Field</code> constraints for validation; never put business rules in the model.</li>
|
||||||
<li><strong>Inherit from BaseDocument</strong> for all models to get auto-timestamps</li>
|
<li><strong>Use services for cross-repository logic.</strong> A <code>Service</code> composes multiple repositories (and <code>TransactionManager</code>) — routes stay thin.</li>
|
||||||
<li><strong>Create custom repository classes</strong> for business logic instead of mixing it with models</li>
|
<li><strong>Expose <code>get_many(filter=..., skip=..., limit=..., sort=...)</code></strong> instead of raw <code>find</code> for list endpoints — you get controlled pagination for free.</li>
|
||||||
<li><strong>Use transactions</strong> for operations that modify multiple documents</li>
|
</ol>
|
||||||
<li><strong>Add indexes</strong> during model registration for frequently queried fields</li>
|
<h2 id="lifecycle">🔄 Lifecycle</h2>
|
||||||
<li><strong>Implement pagination</strong> for list endpoints to avoid performance issues</li>
|
<ol>
|
||||||
<li><strong>Use type hints</strong> for better IDE support and type checking</li>
|
<li><strong>Connect once, in the lifespan.</strong> <code>MongoConnectionManager.lifespan(...)</code> (or explicit <code>connect</code>/<code>disconnect</code>) — never lazily per request.</li>
|
||||||
|
<li><strong>Construct repositories after <code>connect()</code>.</strong> Module-level <code>Repo()</code> before connection raises <code>RuntimeError("Database not connected...")</code>. Use dependencies or construct inside the lifespan/request.</li>
|
||||||
|
<li><strong>Order the cache lifecycle strictly:</strong> <code>set_cache_backend(backend)</code> → <code>initialize_cache()</code> (after connect, before use) → <code>shutdown_cache()</code> on exit.</li>
|
||||||
|
<li><strong>Register all models up front</strong> via <code>ModelRegistry.register(...)</code> and let <code>initialize_all()</code> create indexes once at startup (idempotent).</li>
|
||||||
|
</ol>
|
||||||
|
<h2 id="data-performance">🗄️ Data & Performance</h2>
|
||||||
|
<ol>
|
||||||
|
<li><strong>Declare indexes for every hot query.</strong> Single-field, composite, and optioned (unique/TTL) specs all work via <code>ModelRegistry.register</code> — see <a href="../03_use_cases/13_index_creation/">use case 13</a>.</li>
|
||||||
|
<li><strong>Cache only hot, low-write <code>_id</code> reads.</strong> Use <code>CachedBaseRepository</code> for lookups-by-id; invalidate (<code>update</code>/<code>delete</code> handle it) and pick a sensible <code>default_ttl</code>.</li>
|
||||||
|
<li><strong>Populate at the repository boundary.</strong> <code>PopulatingRepository</code> resolves refs on read and depopulates on write; do not hand-roll joins in endpoints.</li>
|
||||||
|
<li><strong>Respect the populate invariants:</strong> rules name the field that <em>holds</em> the reference and that <em>is replaced</em>; <code>patch()</code> cannot touch FK fields — use <code>update()</code>.</li>
|
||||||
|
</ol>
|
||||||
|
<h2 id="transactions-errors">🔁 Transactions & Errors</h2>
|
||||||
|
<ol>
|
||||||
|
<li><strong>Use transactions for multi-document writes.</strong> <code>TransactionManager.start_session</code> (inline) or <code>execute_transaction</code> (list of ops) — and pass <code>session=</code> to <strong>every</strong> collection call inside.</li>
|
||||||
|
<li><strong>Handle the library's real exceptions</strong> at the edges: <code>DuplicateKeyError</code> → 409, <code>InvalidId</code> → 400, <code>CircularReferenceError</code> → 409, <code>ValueError</code> guides → 400/422 (see <a href="../06_error_handling/">Error Handling</a>).</li>
|
||||||
|
</ol>
|
||||||
|
<h2 id="testing">🧪 Testing</h2>
|
||||||
|
<ol>
|
||||||
|
<li><strong>Default to mock-based unit tests.</strong> Patch <code>MongoConnectionManager.get_database</code>, use <code>AsyncMock</code> collections and cursor chains — the whole suite runs without MongoDB (<a href="../03_use_cases/14_testing_guide/">use case 14</a>).</li>
|
||||||
|
<li><strong>Mirror the library tests.</strong> <code>tests/test_{repository,populating_repository,cache,registry,transactions}.py</code> are canonical examples of every pattern above.</li>
|
||||||
|
<li><strong>Use type hints end-to-end</strong> — mypy-gated CI (see pyproject) catches drift early.</li>
|
||||||
</ol>
|
</ol>
|
||||||
<hr />
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../01_overview/">Overview</a> · <a href="../02_components/">Core Components</a> · <a href="../#documentation-structure">Use Cases</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -454,7 +454,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -883,6 +883,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||||
|
|
||||||
|
|
||||||
@@ -915,6 +917,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__title" for="__toc">
|
<label class="md-nav__title" for="__toc">
|
||||||
<span class="md-nav__icon md-icon"></span>
|
<span class="md-nav__icon md-icon"></span>
|
||||||
Table of contents
|
Table of contents
|
||||||
@@ -922,16 +926,6 @@
|
|||||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#common-patterns" class="md-nav__link">
|
|
||||||
<span class="md-ellipsis">
|
|
||||||
Common Patterns
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<nav class="md-nav" aria-label="Common Patterns">
|
|
||||||
<ul class="md-nav__list">
|
|
||||||
|
|
||||||
<li class="md-nav__item">
|
|
||||||
<a href="#pattern-1-service-layer-with-repository" class="md-nav__link">
|
<a href="#pattern-1-service-layer-with-repository" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Pattern 1: Service Layer with Repository
|
Pattern 1: Service Layer with Repository
|
||||||
@@ -940,7 +934,7 @@
|
|||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#pattern-2-aggregation-pipeline" class="md-nav__link">
|
<a href="#pattern-2-aggregation-pipeline" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Pattern 2: Aggregation Pipeline
|
Pattern 2: Aggregation Pipeline
|
||||||
@@ -949,7 +943,7 @@
|
|||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#pattern-3-bulk-operations" class="md-nav__link">
|
<a href="#pattern-3-bulk-operations" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Pattern 3: Bulk Operations
|
Pattern 3: Bulk Operations
|
||||||
@@ -958,8 +952,21 @@
|
|||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
<li class="md-nav__item">
|
||||||
</nav>
|
<a href="#pattern-4-transactional-multi-step-write" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Pattern 4: Transactional Multi-Step Write
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
@@ -1031,47 +1038,103 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1>Common Patterns</h1>
|
<h1 id="common-patterns">Common Patterns</h1>
|
||||||
|
<p>Reusable code shapes built on <code>BaseRepository</code> and friends.</p>
|
||||||
<h2 id="common-patterns">Common Patterns</h2>
|
<hr />
|
||||||
<h3 id="pattern-1-service-layer-with-repository">Pattern 1: Service Layer with Repository</h3>
|
<h2 id="pattern-1-service-layer-with-repository">Pattern 1: Service Layer with Repository</h2>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="k">class</span><span class="w"> </span><span class="nc">UserService</span><span class="p">:</span>
|
<p>Keep orchestration out of routes. A service composes one or more repositories and owns the domain rules.</p>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user_repo</span><span class="p">:</span> <span class="n">UserRepository</span><span class="p">):</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseRepository</span><span class="p">,</span> <span class="n">BaseDocument</span>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a> <span class="bp">self</span><span class="o">.</span><span class="n">user_repo</span> <span class="o">=</span> <span class="n">user_repo</span>
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a>
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">register_user</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">User</span><span class="p">:</span>
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a> <span class="c1"># Check if user exists</span>
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a> <span class="n">existing</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">user_repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">find_one</span><span class="p">({</span><span class="s2">"email"</span><span class="p">:</span> <span class="n">email</span><span class="p">})</span>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="k">if</span> <span class="n">existing</span><span class="p">:</span>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"Email already exists"</span><span class="p">)</span>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="k">class</span><span class="w"> </span><span class="nc">UserRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">User</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> <span class="c1"># Create user</span>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> <span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="n">username</span><span class="o">=</span><span class="n">username</span><span class="p">,</span> <span class="n">email</span><span class="o">=</span><span class="n">email</span><span class="p">)</span>
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">User</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">user_repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a>
|
||||||
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">find_by_email</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">User</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">doc</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">find_one</span><span class="p">({</span><span class="s2">"email"</span><span class="p">:</span> <span class="n">email</span><span class="p">})</span>
|
||||||
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="p">(</span><span class="o">**</span><span class="n">doc</span><span class="p">)</span> <span class="k">if</span> <span class="n">doc</span> <span class="k">else</span> <span class="kc">None</span>
|
||||||
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a>
|
||||||
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a>
|
||||||
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a><span class="k">class</span><span class="w"> </span><span class="nc">UserService</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user_repo</span><span class="p">:</span> <span class="n">UserRepository</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> <span class="bp">self</span><span class="o">.</span><span class="n">user_repo</span> <span class="o">=</span> <span class="n">user_repo</span>
|
||||||
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a>
|
||||||
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">register_user</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">User</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="k">if</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">user_repo</span><span class="o">.</span><span class="n">find_by_email</span><span class="p">(</span><span class="n">email</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"Email already exists"</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">user_repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">User</span><span class="p">(</span><span class="n">username</span><span class="o">=</span><span class="n">username</span><span class="p">,</span> <span class="n">email</span><span class="o">=</span><span class="n">email</span><span class="p">))</span>
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
<h3 id="pattern-2-aggregation-pipeline">Pattern 2: Aggregation Pipeline</h3>
|
<blockquote>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_user_stats</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">dict</span><span class="p">:</span>
|
<p>Register a unique index on <code>email</code> via <code>ModelRegistry</code> so the race is also caught by <code>DuplicateKeyError</code> — see <a href="../03_use_cases/13_index_creation/">use case 13</a>.</p>
|
||||||
<a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a> <span class="n">pipeline</span> <span class="o">=</span> <span class="p">[</span>
|
</blockquote>
|
||||||
<a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a> <span class="p">{</span><span class="s2">"$match"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"author_id"</span><span class="p">:</span> <span class="n">user_id</span><span class="p">}},</span>
|
<hr />
|
||||||
<a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a> <span class="p">{</span><span class="s2">"$group"</span><span class="p">:</span> <span class="p">{</span>
|
<h2 id="pattern-2-aggregation-pipeline">Pattern 2: Aggregation Pipeline</h2>
|
||||||
<a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a> <span class="s2">"_id"</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
|
<p>Aggregations hit the raw Motor collection — wrap them in a repository method so callers stay at the domain level.</p>
|
||||||
<a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a> <span class="s2">"total_posts"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"$sum"</span><span class="p">:</span> <span class="mi">1</span><span class="p">},</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseRepository</span><span class="p">,</span> <span class="n">BaseDocument</span>
|
||||||
<a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a> <span class="s2">"total_likes"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"$sum"</span><span class="p">:</span> <span class="s2">"$likes"</span><span class="p">}</span>
|
<a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a>
|
||||||
<a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></a> <span class="p">}}</span>
|
<a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a>
|
||||||
<a id="__codelineno-1-9" name="__codelineno-1-9" href="#__codelineno-1-9"></a> <span class="p">]</span>
|
<a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a><span class="k">class</span><span class="w"> </span><span class="nc">Post</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">post_repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">aggregate</span><span class="p">(</span><span class="n">pipeline</span><span class="p">)</span><span class="o">.</span><span class="n">to_list</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
<a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a> <span class="n">author_id</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-1-11" name="__codelineno-1-11" href="#__codelineno-1-11"></a> <span class="k">return</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">result</span> <span class="k">else</span> <span class="p">{</span><span class="s2">"total_posts"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">"total_likes"</span><span class="p">:</span> <span class="mi">0</span><span class="p">}</span>
|
<a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
</code></pre></div>
|
<a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a> <span class="n">likes</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
|
||||||
<h3 id="pattern-3-bulk-operations">Pattern 3: Bulk Operations</h3>
|
<a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></a>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">bulk_update_status</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">ids</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span> <span class="n">status</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
<a id="__codelineno-1-9" name="__codelineno-1-9" href="#__codelineno-1-9"></a>
|
||||||
<a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a> <span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
<a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a><span class="k">class</span><span class="w"> </span><span class="nc">PostRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Post</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a> <span class="n">object_ids</span> <span class="o">=</span> <span class="p">[</span><span class="n">ObjectId</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span> <span class="k">for</span> <span class="nb">id</span> <span class="ow">in</span> <span class="n">ids</span><span class="p">]</span>
|
<a id="__codelineno-1-11" name="__codelineno-1-11" href="#__codelineno-1-11"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">update_many</span><span class="p">(</span>
|
<a id="__codelineno-1-12" name="__codelineno-1-12" href="#__codelineno-1-12"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"posts"</span><span class="p">,</span> <span class="n">Post</span><span class="p">)</span>
|
||||||
<a id="__codelineno-2-5" name="__codelineno-2-5" href="#__codelineno-2-5"></a> <span class="p">{</span><span class="s2">"_id"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"$in"</span><span class="p">:</span> <span class="n">object_ids</span><span class="p">}},</span>
|
<a id="__codelineno-1-13" name="__codelineno-1-13" href="#__codelineno-1-13"></a>
|
||||||
<a id="__codelineno-2-6" name="__codelineno-2-6" href="#__codelineno-2-6"></a> <span class="p">{</span><span class="s2">"$set"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"status"</span><span class="p">:</span> <span class="n">status</span><span class="p">,</span> <span class="s2">"updated_at"</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()}}</span>
|
<a id="__codelineno-1-14" name="__codelineno-1-14" href="#__codelineno-1-14"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_user_stats</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">dict</span><span class="p">:</span>
|
||||||
<a id="__codelineno-2-7" name="__codelineno-2-7" href="#__codelineno-2-7"></a> <span class="p">)</span>
|
<a id="__codelineno-1-15" name="__codelineno-1-15" href="#__codelineno-1-15"></a> <span class="n">pipeline</span> <span class="o">=</span> <span class="p">[</span>
|
||||||
|
<a id="__codelineno-1-16" name="__codelineno-1-16" href="#__codelineno-1-16"></a> <span class="p">{</span><span class="s2">"$match"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"author_id"</span><span class="p">:</span> <span class="n">user_id</span><span class="p">}},</span>
|
||||||
|
<a id="__codelineno-1-17" name="__codelineno-1-17" href="#__codelineno-1-17"></a> <span class="p">{</span><span class="s2">"$group"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"_id"</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="s2">"total_posts"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"$sum"</span><span class="p">:</span> <span class="mi">1</span><span class="p">},</span> <span class="s2">"total_likes"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"$sum"</span><span class="p">:</span> <span class="s2">"$likes"</span><span class="p">}}},</span>
|
||||||
|
<a id="__codelineno-1-18" name="__codelineno-1-18" href="#__codelineno-1-18"></a> <span class="p">]</span>
|
||||||
|
<a id="__codelineno-1-19" name="__codelineno-1-19" href="#__codelineno-1-19"></a> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">aggregate</span><span class="p">(</span><span class="n">pipeline</span><span class="p">)</span><span class="o">.</span><span class="n">to_list</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-1-20" name="__codelineno-1-20" href="#__codelineno-1-20"></a> <span class="k">return</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">result</span> <span class="k">else</span> <span class="p">{</span><span class="s2">"total_posts"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">"total_likes"</span><span class="p">:</span> <span class="mi">0</span><span class="p">}</span>
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
<hr />
|
<hr />
|
||||||
|
<h2 id="pattern-3-bulk-operations">Pattern 3: Bulk Operations</h2>
|
||||||
|
<p>Multi-document mutation in one call — again wrapped at the repository boundary.</p>
|
||||||
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">datetime</span><span class="w"> </span><span class="kn">import</span> <span class="n">datetime</span>
|
||||||
|
<a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a>
|
||||||
|
<a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
||||||
|
<a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseRepository</span><span class="p">,</span> <span class="n">BaseDocument</span>
|
||||||
|
<a id="__codelineno-2-5" name="__codelineno-2-5" href="#__codelineno-2-5"></a>
|
||||||
|
<a id="__codelineno-2-6" name="__codelineno-2-6" href="#__codelineno-2-6"></a>
|
||||||
|
<a id="__codelineno-2-7" name="__codelineno-2-7" href="#__codelineno-2-7"></a><span class="k">class</span><span class="w"> </span><span class="nc">Task</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-2-8" name="__codelineno-2-8" href="#__codelineno-2-8"></a> <span class="n">status</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"pending"</span>
|
||||||
|
<a id="__codelineno-2-9" name="__codelineno-2-9" href="#__codelineno-2-9"></a>
|
||||||
|
<a id="__codelineno-2-10" name="__codelineno-2-10" href="#__codelineno-2-10"></a>
|
||||||
|
<a id="__codelineno-2-11" name="__codelineno-2-11" href="#__codelineno-2-11"></a><span class="k">class</span><span class="w"> </span><span class="nc">TaskRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">Task</span><span class="p">]):</span>
|
||||||
|
<a id="__codelineno-2-12" name="__codelineno-2-12" href="#__codelineno-2-12"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
|
<a id="__codelineno-2-13" name="__codelineno-2-13" href="#__codelineno-2-13"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"tasks"</span><span class="p">,</span> <span class="n">Task</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-2-14" name="__codelineno-2-14" href="#__codelineno-2-14"></a>
|
||||||
|
<a id="__codelineno-2-15" name="__codelineno-2-15" href="#__codelineno-2-15"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">bulk_update_status</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">ids</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span> <span class="n">status</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-2-16" name="__codelineno-2-16" href="#__codelineno-2-16"></a> <span class="n">object_ids</span> <span class="o">=</span> <span class="p">[</span><span class="n">ObjectId</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">ids</span><span class="p">]</span>
|
||||||
|
<a id="__codelineno-2-17" name="__codelineno-2-17" href="#__codelineno-2-17"></a> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">update_many</span><span class="p">(</span>
|
||||||
|
<a id="__codelineno-2-18" name="__codelineno-2-18" href="#__codelineno-2-18"></a> <span class="p">{</span><span class="s2">"_id"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"$in"</span><span class="p">:</span> <span class="n">object_ids</span><span class="p">}},</span>
|
||||||
|
<a id="__codelineno-2-19" name="__codelineno-2-19" href="#__codelineno-2-19"></a> <span class="p">{</span><span class="s2">"$set"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"status"</span><span class="p">:</span> <span class="n">status</span><span class="p">,</span> <span class="s2">"updated_at"</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()}},</span>
|
||||||
|
<a id="__codelineno-2-20" name="__codelineno-2-20" href="#__codelineno-2-20"></a> <span class="p">)</span>
|
||||||
|
<a id="__codelineno-2-21" name="__codelineno-2-21" href="#__codelineno-2-21"></a> <span class="k">return</span> <span class="n">result</span><span class="o">.</span><span class="n">modified_count</span>
|
||||||
|
</code></pre></div>
|
||||||
|
<blockquote>
|
||||||
|
<p>Bulk/aggregation methods bypass the caching and population layers — keep them deliberate and documented.</p>
|
||||||
|
</blockquote>
|
||||||
|
<hr />
|
||||||
|
<h2 id="pattern-4-transactional-multi-step-write">Pattern 4: Transactional Multi-Step Write</h2>
|
||||||
|
<p>Cross-collection atomicity with <code>execute_transaction</code> — see <a href="../03_use_cases/12_transaction_helper/">use case 12</a> for the full code.</p>
|
||||||
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="n">results</span> <span class="o">=</span> <span class="k">await</span> <span class="n">TransactionManager</span><span class="o">.</span><span class="n">execute_transaction</span><span class="p">([</span><span class="n">op1</span><span class="p">,</span> <span class="n">op2</span><span class="p">,</span> <span class="n">op3</span><span class="p">])</span>
|
||||||
|
<a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a><span class="c1"># op1/op2/op3 each receive the shared session; one failure rolls back all.</span>
|
||||||
|
</code></pre></div>
|
||||||
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../03_use_cases/02_custom_repo/">02 – Custom repository</a> · <a href="../03_use_cases/03_transactions/">03 – Transactions</a> · <a href="../04_best_practices/">Best Practices</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -454,7 +454,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -905,6 +905,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||||
|
|
||||||
|
|
||||||
@@ -937,6 +939,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__title" for="__toc">
|
<label class="md-nav__title" for="__toc">
|
||||||
<span class="md-nav__icon md-icon"></span>
|
<span class="md-nav__icon md-icon"></span>
|
||||||
Table of contents
|
Table of contents
|
||||||
@@ -944,9 +948,36 @@
|
|||||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#error-handling" class="md-nav__link">
|
<a href="#library-raised-exceptions" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Error Handling
|
📋 Library-Raised Exceptions
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#fastapi-mapping-example" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
🚀 FastAPI Mapping Example
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#tips" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
💡 Tips
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -998,33 +1029,145 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1>Error Handling</h1>
|
<h1 id="error-handling">Error Handling</h1>
|
||||||
|
<p>What can raise, what it means, and how to map it in a FastAPI app.</p>
|
||||||
<h2 id="error-handling">Error Handling</h2>
|
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">HTTPException</span>
|
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">pymongo.errors</span><span class="w"> </span><span class="kn">import</span> <span class="n">DuplicateKeyError</span>
|
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson.errors</span><span class="w"> </span><span class="kn">import</span> <span class="n">InvalidId</span>
|
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a>
|
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/users/"</span><span class="p">)</span>
|
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_user</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span><span class="p">):</span>
|
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a> <span class="k">try</span><span class="p">:</span>
|
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">user_repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> <span class="k">except</span> <span class="n">DuplicateKeyError</span><span class="p">:</span>
|
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">409</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"User already exists"</span><span class="p">)</span>
|
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
|
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a>
|
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/users/</span><span class="si">{user_id}</span><span class="s2">"</span><span class="p">)</span>
|
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_user</span><span class="p">(</span><span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="k">try</span><span class="p">:</span>
|
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">user_repo</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
|
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="p">:</span>
|
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"User not found"</span><span class="p">)</span>
|
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> <span class="k">return</span> <span class="n">user</span>
|
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> <span class="k">except</span> <span class="n">InvalidId</span><span class="p">:</span>
|
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"Invalid user ID format"</span><span class="p">)</span>
|
|
||||||
</code></pre></div>
|
|
||||||
<hr />
|
<hr />
|
||||||
|
<h2 id="library-raised-exceptions">📋 Library-Raised Exceptions</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Exception</th>
|
||||||
|
<th>Source</th>
|
||||||
|
<th>Meaning / fix</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>RuntimeError("Database not connected. Call connect() first.")</code></td>
|
||||||
|
<td><code>get_database()</code> / <code>get_client()</code> and any repository constructed first</td>
|
||||||
|
<td><code>MongoConnectionManager.connect()</code> hasn't run — wire the lifespan.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>RuntimeError("No cache backend registered. Call set_cache_backend() first.")</code></td>
|
||||||
|
<td><code>ModelRegistry.initialize_cache()</code></td>
|
||||||
|
<td>Call <code>set_cache_backend(backend)</code> before <code>initialize_cache()</code>.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>KeyError("Model for collection '...' not registered")</code></td>
|
||||||
|
<td><code>ModelRegistry.get_model()</code></td>
|
||||||
|
<td>Collection was never <code>register</code>ed (or typo).</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>ValueError("Cannot patch FK fields via patch(): ...")</code></td>
|
||||||
|
<td><code>PopulatingRepository.patch()</code></td>
|
||||||
|
<td><code>patch</code> must not touch populated ref fields — use <code>update()</code> with a model.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>ValueError("...contains embedded dict(s) — run repair script")</code></td>
|
||||||
|
<td><code>_populate</code> on read</td>
|
||||||
|
<td>A FK field holds an embedded document instead of an <code>ObjectId</code> — migrate the data.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>ValueError("...contains ObjectId — was populate skipped?")</code></td>
|
||||||
|
<td><code>_depopulate</code> on write</td>
|
||||||
|
<td>A populate-ruled field is still an <code>ObjectId</code> at depopulate time — the read must have populated it first.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>CircularReferenceError(collection, doc_id, path)</code></td>
|
||||||
|
<td><code>PopulationEngine.populate</code></td>
|
||||||
|
<td>A <code>(Class, id)</code> pair was revisited — raise <code>max_depth</code> or fix the graph.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>ImportError("redis package required ... mongo-ops[redis]")</code></td>
|
||||||
|
<td><code>CacheConfig</code> / <code>RedisCacheBackend</code></td>
|
||||||
|
<td>Missing <code>redis</code> extra.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>ValueError("redis_client required when backend='redis'")</code></td>
|
||||||
|
<td><code>CacheConfig</code></td>
|
||||||
|
<td><code>backend="redis"</code> without a client.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Pymongo <code>DuplicateKeyError</code></td>
|
||||||
|
<td>any insert/update</td>
|
||||||
|
<td>Unique index violation (e.g., duplicate email).</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>bson.errors.InvalidId</code></td>
|
||||||
|
<td><code>ObjectId(...)</code> on a bad string</td>
|
||||||
|
<td>Wrapped by <code>PyObjectId</code> model validation on API inputs.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<blockquote>
|
||||||
|
<p><code>KeyError</code> for <code>ModelRegistry.get_model</code> and the <code>RuntimeError</code>/<code>ValueError</code> guards are <strong>by design</strong> — they fail loudly at startup or first call instead of misbehaving silently.</p>
|
||||||
|
</blockquote>
|
||||||
|
<hr />
|
||||||
|
<h2 id="fastapi-mapping-example">🚀 FastAPI Mapping Example</h2>
|
||||||
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">FastAPI</span><span class="p">,</span> <span class="n">HTTPException</span><span class="p">,</span> <span class="n">Request</span>
|
||||||
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi.responses</span><span class="w"> </span><span class="kn">import</span> <span class="n">JSONResponse</span>
|
||||||
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson.errors</span><span class="w"> </span><span class="kn">import</span> <span class="n">InvalidId</span>
|
||||||
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">pymongo.errors</span><span class="w"> </span><span class="kn">import</span> <span class="n">DuplicateKeyError</span>
|
||||||
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.cache</span><span class="w"> </span><span class="kn">import</span> <span class="n">CircularReferenceError</span>
|
||||||
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>
|
||||||
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a>
|
||||||
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">()</span>
|
||||||
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a>
|
||||||
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a>
|
||||||
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a><span class="nd">@app</span><span class="o">.</span><span class="n">exception_handler</span><span class="p">(</span><span class="n">DuplicateKeyError</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">duplicate_key_handler</span><span class="p">(</span><span class="n">_</span><span class="p">:</span> <span class="n">Request</span><span class="p">,</span> <span class="n">__</span><span class="p">:</span> <span class="n">DuplicateKeyError</span><span class="p">)</span> <span class="o">-></span> <span class="n">JSONResponse</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="k">return</span> <span class="n">JSONResponse</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">409</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="p">{</span><span class="s2">"detail"</span><span class="p">:</span> <span class="s2">"Resource already exists"</span><span class="p">})</span>
|
||||||
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a>
|
||||||
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a>
|
||||||
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a><span class="nd">@app</span><span class="o">.</span><span class="n">exception_handler</span><span class="p">(</span><span class="n">InvalidId</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">invalid_id_handler</span><span class="p">(</span><span class="n">_</span><span class="p">:</span> <span class="n">Request</span><span class="p">,</span> <span class="n">__</span><span class="p">:</span> <span class="n">InvalidId</span><span class="p">)</span> <span class="o">-></span> <span class="n">JSONResponse</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="k">return</span> <span class="n">JSONResponse</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="p">{</span><span class="s2">"detail"</span><span class="p">:</span> <span class="s2">"Invalid ID format"</span><span class="p">})</span>
|
||||||
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a>
|
||||||
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a>
|
||||||
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="nd">@app</span><span class="o">.</span><span class="n">exception_handler</span><span class="p">(</span><span class="n">CircularReferenceError</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">circular_ref_handler</span><span class="p">(</span><span class="n">_</span><span class="p">:</span> <span class="n">Request</span><span class="p">,</span> <span class="n">exc</span><span class="p">:</span> <span class="n">CircularReferenceError</span><span class="p">)</span> <span class="o">-></span> <span class="n">JSONResponse</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="k">return</span> <span class="n">JSONResponse</span><span class="p">(</span>
|
||||||
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="n">status_code</span><span class="o">=</span><span class="mi">409</span><span class="p">,</span>
|
||||||
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="n">content</span><span class="o">=</span><span class="p">{</span><span class="s2">"detail"</span><span class="p">:</span> <span class="sa">f</span><span class="s2">"Circular reference detected: </span><span class="si">{</span><span class="n">exc</span><span class="o">.</span><span class="n">path</span><span class="si">}</span><span class="s2">"</span><span class="p">},</span>
|
||||||
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> <span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a>
|
||||||
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a>
|
||||||
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a><span class="c1"># Or handle inline for route-specific responses:</span>
|
||||||
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/users/"</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_user</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span><span class="p">,</span> <span class="n">repo</span><span class="o">=</span><span class="n">Depends</span><span class="p">(</span><span class="n">get_user_repository</span><span class="p">)):</span>
|
||||||
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="k">try</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="k">except</span> <span class="n">DuplicateKeyError</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">409</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"User already exists"</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">exc</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">exc</span><span class="p">))</span>
|
||||||
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a>
|
||||||
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a>
|
||||||
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"/users/</span><span class="si">{user_id}</span><span class="s2">"</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_user</span><span class="p">(</span><span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">repo</span><span class="o">=</span><span class="n">Depends</span><span class="p">(</span><span class="n">get_user_repository</span><span class="p">)):</span>
|
||||||
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="k">try</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="k">except</span> <span class="n">InvalidId</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"Invalid user ID"</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="p">:</span>
|
||||||
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"User not found"</span><span class="p">)</span>
|
||||||
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="k">return</span> <span class="n">user</span>
|
||||||
|
</code></pre></div>
|
||||||
|
<blockquote>
|
||||||
|
<p>Prefer exception <strong>handlers</strong> for the library-level exceptions (409/400 above) and per-endpoint <code>try/except</code> for domain decisions (404).</p>
|
||||||
|
</blockquote>
|
||||||
|
<hr />
|
||||||
|
<h2 id="tips">💡 Tips</h2>
|
||||||
|
<ul>
|
||||||
|
<li>The <code>ValueError</code> populate guards are your friends: they surface data-shape drift (embedded docs, skipped population) at the exact call site.</li>
|
||||||
|
<li>In dev, log the <code>CircularReferenceError.path</code> — it prints the visited <code>Class:id</code> chain.</li>
|
||||||
|
<li>Never swallow <code>RuntimeError</code>s at startup; let the app fail to load so the misconfiguration is obvious.</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../03_use_cases/03_transactions/">03 – Transactions</a> · <a href="../03_use_cases/08_population/">08 – Population</a> · <a href="../03_use_cases/11_cache_lifecycle/">11 – Cache lifecycle</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -452,7 +452,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -925,6 +925,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||||
|
|
||||||
|
|
||||||
@@ -957,6 +959,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<label class="md-nav__title" for="__toc">
|
<label class="md-nav__title" for="__toc">
|
||||||
<span class="md-nav__icon md-icon"></span>
|
<span class="md-nav__icon md-icon"></span>
|
||||||
Table of contents
|
Table of contents
|
||||||
@@ -964,9 +968,27 @@
|
|||||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#testing-example" class="md-nav__link">
|
<a href="#mock-based-quickstart" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Testing Example
|
🚀 Mock-Based Quickstart
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#notes" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
💡 Notes
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#related" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Related
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -996,55 +1018,79 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1>Testing Example</h1>
|
<h1 id="testing-example">Testing Example</h1>
|
||||||
|
<p>A zero-MongoDB unit-test quickstart. The full guide is <a href="../03_use_cases/14_testing_guide/">use case 14</a>.</p>
|
||||||
<h2 id="testing-example">Testing Example</h2>
|
<hr />
|
||||||
|
<h2 id="mock-based-quickstart">🚀 Mock-Based Quickstart</h2>
|
||||||
|
<p>Patch the connection manager and stub the collection — no network, no docker.</p>
|
||||||
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">import</span><span class="w"> </span><span class="nn">pytest</span>
|
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">import</span><span class="w"> </span><span class="nn">pytest</span>
|
||||||
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="p">(</span>
|
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">unittest.mock</span><span class="w"> </span><span class="kn">import</span> <span class="n">AsyncMock</span><span class="p">,</span> <span class="n">patch</span>
|
||||||
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a> <span class="n">MongoConnectionManager</span><span class="p">,</span>
|
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>
|
||||||
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a> <span class="n">BaseDocument</span><span class="p">,</span>
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">bson</span><span class="w"> </span><span class="kn">import</span> <span class="n">ObjectId</span>
|
||||||
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a> <span class="n">BaseRepository</span><span class="p">,</span>
|
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseDocument</span><span class="p">,</span> <span class="n">BaseRepository</span>
|
||||||
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="p">)</span>
|
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.repository</span><span class="w"> </span><span class="kn">import</span> <span class="n">MongoConnectionManager</span>
|
||||||
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="kn">from</span><span class="w"> </span><span class="nn">pydantic</span><span class="w"> </span><span class="kn">import</span> <span class="n">Field</span>
|
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a>
|
||||||
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Optional</span>
|
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a>
|
||||||
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a>
|
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a><span class="c1"># Define Model</span>
|
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">BaseDocument</span><span class="p">):</span>
|
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="o">...</span><span class="p">,</span> <span class="n">min_length</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
|
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a>
|
||||||
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
|
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a>
|
||||||
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> <span class="n">is_active</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span>
|
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a><span class="k">class</span><span class="w"> </span><span class="nc">UserRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">User</span><span class="p">]):</span>
|
||||||
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a>
|
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a><span class="c1"># Create Repository</span>
|
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">User</span><span class="p">)</span>
|
||||||
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a><span class="k">class</span><span class="w"> </span><span class="nc">UserRepository</span><span class="p">(</span><span class="n">BaseRepository</span><span class="p">[</span><span class="n">User</span><span class="p">]):</span>
|
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a>
|
||||||
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a>
|
||||||
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="s2">"users"</span><span class="p">,</span> <span class="n">User</span><span class="p">)</span>
|
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
|
||||||
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a>
|
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a><span class="k">def</span><span class="w"> </span><span class="nf">repo</span><span class="p">():</span>
|
||||||
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a>
|
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> <span class="n">mock_collection</span> <span class="o">=</span> <span class="n">AsyncMock</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
|
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> <span class="k">with</span> <span class="n">patch</span><span class="o">.</span><span class="n">object</span><span class="p">(</span><span class="n">MongoConnectionManager</span><span class="p">,</span> <span class="s2">"get_database"</span><span class="p">)</span> <span class="k">as</span> <span class="n">mock_db</span><span class="p">:</span>
|
||||||
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">db</span><span class="p">():</span>
|
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> <span class="n">mock_db</span><span class="o">.</span><span class="n">return_value</span><span class="o">.</span><span class="fm">__getitem__</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="n">mock_collection</span>
|
||||||
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="k">await</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span>
|
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> <span class="n">r</span> <span class="o">=</span> <span class="n">UserRepository</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="n">uri</span><span class="o">=</span><span class="s2">"mongodb://localhost:27017"</span><span class="p">,</span>
|
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> <span class="n">r</span><span class="o">.</span><span class="n">collection</span> <span class="o">=</span> <span class="n">mock_collection</span>
|
||||||
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> <span class="n">db_name</span><span class="o">=</span><span class="s2">"test_db"</span>
|
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> <span class="k">return</span> <span class="n">r</span>
|
||||||
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a> <span class="p">)</span>
|
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a>
|
||||||
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a> <span class="k">yield</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">get_database</span><span class="p">()</span>
|
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a>
|
||||||
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a> <span class="k">await</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">get_client</span><span class="p">()</span><span class="o">.</span><span class="n">drop_database</span><span class="p">(</span><span class="s2">"test_db"</span><span class="p">)</span>
|
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">asyncio</span>
|
||||||
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a> <span class="k">await</span> <span class="n">MongoConnectionManager</span><span class="o">.</span><span class="n">disconnect</span><span class="p">()</span>
|
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">test_create_user</span><span class="p">(</span><span class="n">repo</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a>
|
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a> <span class="n">mock_collection</span> <span class="o">=</span> <span class="n">repo</span><span class="o">.</span><span class="n">collection</span>
|
||||||
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">asyncio</span>
|
<a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> <span class="n">oid</span> <span class="o">=</span> <span class="n">ObjectId</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">test_create_user</span><span class="p">(</span><span class="n">db</span><span class="p">):</span>
|
<a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> <span class="n">mock_collection</span><span class="o">.</span><span class="n">insert_one</span><span class="o">.</span><span class="n">return_value</span><span class="o">.</span><span class="n">inserted_id</span> <span class="o">=</span> <span class="n">oid</span>
|
||||||
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> <span class="n">repo</span> <span class="o">=</span> <span class="n">UserRepository</span><span class="p">()</span>
|
<a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a>
|
||||||
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">User</span><span class="p">(</span><span class="n">username</span><span class="o">=</span><span class="s2">"test"</span><span class="p">,</span> <span class="n">email</span><span class="o">=</span><span class="s2">"test@example.com"</span><span class="p">))</span>
|
<a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> <span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">User</span><span class="p">(</span><span class="n">username</span><span class="o">=</span><span class="s2">"test"</span><span class="p">,</span> <span class="n">email</span><span class="o">=</span><span class="s2">"test@example.com"</span><span class="p">))</span>
|
||||||
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> <span class="k">assert</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
|
<a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a>
|
||||||
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="k">assert</span> <span class="n">user</span><span class="o">.</span><span class="n">username</span> <span class="o">==</span> <span class="s2">"test"</span>
|
<a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> <span class="k">assert</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="n">oid</span>
|
||||||
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a>
|
<a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> <span class="k">assert</span> <span class="n">user</span><span class="o">.</span><span class="n">username</span> <span class="o">==</span> <span class="s2">"test"</span>
|
||||||
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">asyncio</span>
|
<a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a>
|
||||||
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">test_get_by_id</span><span class="p">(</span><span class="n">db</span><span class="p">):</span>
|
<a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a>
|
||||||
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> <span class="n">repo</span> <span class="o">=</span> <span class="n">UserRepository</span><span class="p">()</span>
|
<a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">asyncio</span>
|
||||||
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a> <span class="n">created</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">User</span><span class="p">(</span><span class="n">username</span><span class="o">=</span><span class="s2">"test"</span><span class="p">,</span> <span class="n">email</span><span class="o">=</span><span class="s2">"test@example.com"</span><span class="p">))</span>
|
<a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">test_get_by_id</span><span class="p">(</span><span class="n">repo</span><span class="p">):</span>
|
||||||
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="n">fetched</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">created</span><span class="o">.</span><span class="n">id</span><span class="p">))</span>
|
<a id="__codelineno-0-43" name="__codelineno-0-43" href="#__codelineno-0-43"></a> <span class="n">oid</span> <span class="o">=</span> <span class="n">ObjectId</span><span class="p">()</span>
|
||||||
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="k">assert</span> <span class="n">fetched</span><span class="o">.</span><span class="n">username</span> <span class="o">==</span> <span class="n">created</span><span class="o">.</span><span class="n">username</span>
|
<a id="__codelineno-0-44" name="__codelineno-0-44" href="#__codelineno-0-44"></a> <span class="n">repo</span><span class="o">.</span><span class="n">collection</span><span class="o">.</span><span class="n">find_one</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="p">{</span>
|
||||||
|
<a id="__codelineno-0-45" name="__codelineno-0-45" href="#__codelineno-0-45"></a> <span class="s2">"_id"</span><span class="p">:</span> <span class="n">oid</span><span class="p">,</span>
|
||||||
|
<a id="__codelineno-0-46" name="__codelineno-0-46" href="#__codelineno-0-46"></a> <span class="s2">"username"</span><span class="p">:</span> <span class="s2">"test"</span><span class="p">,</span>
|
||||||
|
<a id="__codelineno-0-47" name="__codelineno-0-47" href="#__codelineno-0-47"></a> <span class="s2">"email"</span><span class="p">:</span> <span class="s2">"test@example.com"</span><span class="p">,</span>
|
||||||
|
<a id="__codelineno-0-48" name="__codelineno-0-48" href="#__codelineno-0-48"></a> <span class="s2">"created_at"</span><span class="p">:</span> <span class="s2">"2024-01-01T00:00:00"</span><span class="p">,</span>
|
||||||
|
<a id="__codelineno-0-49" name="__codelineno-0-49" href="#__codelineno-0-49"></a> <span class="s2">"updated_at"</span><span class="p">:</span> <span class="s2">"2024-01-01T00:00:00"</span><span class="p">,</span>
|
||||||
|
<a id="__codelineno-0-50" name="__codelineno-0-50" href="#__codelineno-0-50"></a> <span class="p">}</span>
|
||||||
|
<a id="__codelineno-0-51" name="__codelineno-0-51" href="#__codelineno-0-51"></a>
|
||||||
|
<a id="__codelineno-0-52" name="__codelineno-0-52" href="#__codelineno-0-52"></a> <span class="n">fetched</span> <span class="o">=</span> <span class="k">await</span> <span class="n">repo</span><span class="o">.</span><span class="n">get_by_id</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">oid</span><span class="p">))</span>
|
||||||
|
<a id="__codelineno-0-53" name="__codelineno-0-53" href="#__codelineno-0-53"></a>
|
||||||
|
<a id="__codelineno-0-54" name="__codelineno-0-54" href="#__codelineno-0-54"></a> <span class="k">assert</span> <span class="n">fetched</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
|
||||||
|
<a id="__codelineno-0-55" name="__codelineno-0-55" href="#__codelineno-0-55"></a> <span class="k">assert</span> <span class="n">fetched</span><span class="o">.</span><span class="n">username</span> <span class="o">==</span> <span class="s2">"test"</span>
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
<hr />
|
<hr />
|
||||||
|
<h2 id="notes">💡 Notes</h2>
|
||||||
|
<ul>
|
||||||
|
<li><code>from mongo_ops.repository import MongoConnectionManager</code> — patch where it is <strong>used</strong> (<code>mongo_ops.repository.MongoConnectionManager</code>), matching the library's own tests.</li>
|
||||||
|
<li>pytest-asyncio runs as <code>auto</code> mode per <code>pyproject.toml</code>, so <code>@pytest.mark.asyncio</code> works without extra config.</li>
|
||||||
|
<li>For population, cache, registry, and transaction mockups — see the <a href="../03_use_cases/14_testing_guide/">full testing guide</a>.</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2 id="related">Related</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../03_use_cases/14_testing_guide/">14 – Testing guide</a> · <a href="../04_best_practices/">Best Practices</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -443,7 +443,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|||||||
@@ -530,7 +530,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
02 components
|
Core Components
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -1028,26 +1028,33 @@
|
|||||||
|
|
||||||
|
|
||||||
<h1 id="mongo-ops-async-mongodb-operations-layer-for-fastapi">🧩 mongo-ops — Async MongoDB Operations Layer for FastAPI</h1>
|
<h1 id="mongo-ops-async-mongodb-operations-layer-for-fastapi">🧩 mongo-ops — Async MongoDB Operations Layer for FastAPI</h1>
|
||||||
<p><code>mongo-ops</code> is a modular, high-performance MongoDB operations library designed for FastAPI microservices.
|
<p><code>mongo-ops</code> is a modular, high-performance MongoDB operations library for FastAPI microservices. It standardizes connection lifecycle, Pydantic v2 document models, and repository-style async CRUD — with added layers for caching, reference population, and multi-document transactions. It is built on top of Motor.</p>
|
||||||
It standardizes repository patterns, async CRUD operations, and model management — with extensible components for both Beanie and Motor.</p>
|
<blockquote>
|
||||||
|
<p><strong>Doc model:</strong> this wiki is written for humans — how‑to guides, examples, and testing recipes. The authoritative API contracts live in the code (docstrings) and the machine‑readable bundle under <code>docs/mcp/</code>.</p>
|
||||||
|
</blockquote>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="key-features">🚀 Key Features</h2>
|
<h2 id="key-features">🚀 Key Features</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>🧱 Unified repository pattern for MongoDB collections</li>
|
<li>🧱 Unified repository pattern — <code>BaseRepository</code> with generic CRUD</li>
|
||||||
<li>⚡ Fully asynchronous (Motor-based)</li>
|
<li>⚡ Fully asynchronous (Motor-based)</li>
|
||||||
<li>🧬 Pydantic v2 data model integration</li>
|
<li>🧬 Pydantic v2 data models with auto‑timestamps (<code>BaseDocument</code>)</li>
|
||||||
<li>🧰 Built-in CRUD and aggregation utilities</li>
|
<li>🔒 Model registry for multi-collection startup (<code>ModelRegistry</code>)</li>
|
||||||
<li>🔒 Transaction and session helpers</li>
|
<li>🗄️ ID-based caching — in-memory or Redis (<code>cache/</code>)</li>
|
||||||
<li>🧩 Optional Beanie ORM integration</li>
|
<li>🔗 Reference population with cycle detection (<code>populate/</code>)</li>
|
||||||
<li>🧪 Pytest-friendly architecture</li>
|
<li>🔁 Transaction and session helpers (<code>transactions/</code>)</li>
|
||||||
|
<li>🧪 Mock-friendly architecture — the whole repo test suite runs without MongoDB</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="installation">📦 Installation</h2>
|
<h2 id="installation">📦 Installation</h2>
|
||||||
<p>From your internal PyPI:</p>
|
<p>From your internal PyPI:</p>
|
||||||
<div class="language-bash highlight"><span class="filename">Bash</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a>pip<span class="w"> </span>install<span class="w"> </span>--extra-index-url<span class="w"> </span>https://<span class="nv">$PYPI_USERNAME</span>:<span class="nv">$PYPI_PASSWORD</span>@pip.aetoskia.com/simple<span class="w"> </span>mongo-ops
|
<div class="language-bash highlight"><span class="filename">Bash</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a>pip<span class="w"> </span>install<span class="w"> </span>--extra-index-url<span class="w"> </span>https://<span class="nv">$PYPI_USERNAME</span>:<span class="nv">$PYPI_PASSWORD</span>@pip.aetoskia.com/simple<span class="w"> </span>mongo-ops
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
|
<p>With the extra batteries your workload needs:</p>
|
||||||
|
<div class="language-bash highlight"><span class="filename">Bash</span><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a>pip<span class="w"> </span>install<span class="w"> </span>--extra-index-url<span class="w"> </span>https://<span class="nv">$PYPI_USERNAME</span>:<span class="nv">$PYPI_PASSWORD</span>@pip.aetoskia.com/simple<span class="w"> </span><span class="s2">"mongo-ops[redis]"</span><span class="w"> </span><span class="c1"># Redis cache backend</span>
|
||||||
|
<a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a>pip<span class="w"> </span>install<span class="w"> </span>--extra-index-url<span class="w"> </span>https://<span class="nv">$PYPI_USERNAME</span>:<span class="nv">$PYPI_PASSWORD</span>@pip.aetoskia.com/simple<span class="w"> </span><span class="s2">"mongo-ops[all]"</span><span class="w"> </span><span class="c1"># dev, redis, beanie, fastapi</span>
|
||||||
|
</code></pre></div>
|
||||||
<p>From local source:</p>
|
<p>From local source:</p>
|
||||||
<div class="language-bash highlight"><span class="filename">Bash</span><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a>pip<span class="w"> </span>install<span class="w"> </span>-e<span class="w"> </span>.
|
<div class="language-bash highlight"><span class="filename">Bash</span><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a>pip<span class="w"> </span>install<span class="w"> </span>-e<span class="w"> </span>.
|
||||||
</code></pre></div>
|
</code></pre></div>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="documentation-structure">📁 Documentation Structure</h2>
|
<h2 id="documentation-structure">📁 Documentation Structure</h2>
|
||||||
@@ -1061,31 +1068,87 @@ It standardizes repository patterns, async CRUD operations, and model management
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="01_overview/">Overview</a></td>
|
<td><a href="01_overview/">Overview</a></td>
|
||||||
<td>Core concept and architecture overview</td>
|
<td>Architecture, lifecycle, and repository decision table</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="02_components/">Core Components</a></td>
|
<td><a href="02_components/">Core Components</a></td>
|
||||||
<td><code>BaseDocument</code>, <code>MongoConnectionManager</code>, <code>BaseRepository</code></td>
|
<td>Validated reference for the public API surface</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="03_use_cases/01_basic_crud/">Use Cases</a></td>
|
<td><strong>Use cases</strong></td>
|
||||||
<td>CRUD, transactions, pagination, and more</td>
|
<td>Step-by-step recipes</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>· <a href="03_use_cases/01_basic_crud/">01 – Basic CRUD API</a></td>
|
||||||
|
<td>FastAPI user management app</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>· <a href="03_use_cases/02_custom_repo/">02 – Custom repository</a></td>
|
||||||
|
<td>Product catalog with business logic</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>· <a href="03_use_cases/03_transactions/">03 – Transactions</a></td>
|
||||||
|
<td>Atomic order + inventory updates</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>· <a href="03_use_cases/04_pagination/">04 – Pagination</a></td>
|
||||||
|
<td>Filtering, sorting, page metadata</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>· <a href="03_use_cases/05_soft_deletes/">05 – Soft deletes</a></td>
|
||||||
|
<td>Recoverable delete pattern</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>· <a href="03_use_cases/06_multi_model/">06 – Multi-model</a></td>
|
||||||
|
<td>One service, many collections</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>· <a href="03_use_cases/07_caching/">07 – Caching</a></td>
|
||||||
|
<td>In-memory / Redis read cache</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>· <a href="03_use_cases/08_population/">08 – Population</a></td>
|
||||||
|
<td>Resolve references on read</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>· <a href="03_use_cases/09_advanced_population/">09 – Advanced population</a></td>
|
||||||
|
<td>Nested + circular references</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>· <a href="03_use_cases/10_cache_and_population/">10 – Cache + population</a></td>
|
||||||
|
<td>Reading through cache, populating on hit</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>· <a href="03_use_cases/11_cache_lifecycle/">11 – Cache lifecycle</a></td>
|
||||||
|
<td>Startup/shutdown hygiene</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>· <a href="03_use_cases/12_transaction_helper/">12 – Transaction helper</a></td>
|
||||||
|
<td><code>execute_transaction</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>· <a href="03_use_cases/13_index_creation/">13 – Indexes</a></td>
|
||||||
|
<td>Single, compound, and optioned indexes</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>· <a href="03_use_cases/14_testing_guide/">14 – Testing guide</a></td>
|
||||||
|
<td>Mock Motor, engine, cache, transactions</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="04_best_practices/">Best Practices</a></td>
|
<td><a href="04_best_practices/">Best Practices</a></td>
|
||||||
<td>Recommended repo structure and patterns</td>
|
<td>Layering, lifecycle, and gotchas</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="05_patterns/">Common Patterns</a></td>
|
<td><a href="05_patterns/">Common Patterns</a></td>
|
||||||
<td>Service layer, aggregation, soft deletes</td>
|
<td>Service layer, aggregation, bulk ops</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="06_error_handling/">Error Handling</a></td>
|
<td><a href="06_error_handling/">Error Handling</a></td>
|
||||||
<td>Centralized error and exception management</td>
|
<td>Library exceptions and FastAPI mapping</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="07_testing_example/">Testing</a></td>
|
<td><a href="07_testing_example/">Testing Example</a></td>
|
||||||
<td>Pytest configuration and fixtures</td>
|
<td>Mock-based quickstart</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -1094,10 +1157,10 @@ It standardizes repository patterns, async CRUD operations, and model management
|
|||||||
<ul>
|
<ul>
|
||||||
<li><strong>Source Code:</strong> <a href="https://git.aetoskia.com/aetos/mongo-ops">Gitea Repository</a></li>
|
<li><strong>Source Code:</strong> <a href="https://git.aetoskia.com/aetos/mongo-ops">Gitea Repository</a></li>
|
||||||
<li><strong>Internal PyPI:</strong> <a href="https://pip.aetoskia.com/simple/mongo-ops">pip.aetoskia.com/simple/mongo-ops</a></li>
|
<li><strong>Internal PyPI:</strong> <a href="https://pip.aetoskia.com/simple/mongo-ops">pip.aetoskia.com/simple/mongo-ops</a></li>
|
||||||
<li><strong>Drone CI:</strong> Auto-builds and publishes tagged releases.</li>
|
<li><strong>Drone CI:</strong> Auto-builds and publishes tagged releases, gated on black / ruff / mypy / pytest.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
<p>© Aetoskia Internal — <code>mongo-ops</code> 0.1.4</p>
|
<p>© Aetoskia Internal — <code>mongo-ops</code> 0.1.5</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Reference in New Issue
Block a user