Files
docs/mongo-ops/03_use_cases/17_populate_depopulate_examples/index.html

1813 lines
67 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="prev" href="../15_populating_repository_wiring/">
<link rel="next" href="../03_transactions/">
<link rel="icon" href="../../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.6.23">
<title>Populate & Depopulate Examples - Mongo Ops Documentation</title>
<link rel="stylesheet" href="../../assets/stylesheets/main.84d31ad4.min.css">
<link rel="stylesheet" href="../../assets/stylesheets/palette.06af60db.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"JetBrains Mono"}</style>
<link rel="stylesheet" href="../../assets/_mkdocstrings.css">
<link rel="stylesheet" href="https://unpkg.com/dracula-prism/dist/css/dracula-prism.css">
<script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
</head>
<body dir="ltr" data-md-color-scheme="slate" data-md-color-primary="blue-grey" data-md-color-accent="teal">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#17-populate-depopulate-worked-examples" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../.." title="Mongo Ops Documentation" class="md-header__button md-logo" aria-label="Mongo Ops Documentation" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 3C7.58 3 4 4.79 4 7s3.58 4 8 4 8-1.79 8-4-3.58-4-8-4M4 9v3c0 2.21 3.58 4 8 4s8-1.79 8-4V9c0 2.21-3.58 4-8 4s-8-1.79-8-4m0 5v3c0 2.21 3.58 4 8 4s8-1.79 8-4v-3c0 2.21-3.58 4-8 4s-8-1.79-8-4"/></svg>
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Mongo Ops Documentation
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Populate & Depopulate Examples
</span>
</div>
</div>
</div>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg>
</a>
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href="../.." class="md-tabs__link">
Home
</a>
</li>
<li class="md-tabs__item">
<a href="../../01_overview/" class="md-tabs__link">
Overview
</a>
</li>
<li class="md-tabs__item">
<a href="../../02_components/" class="md-tabs__link">
Core Components
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="../01_basic_crud/" class="md-tabs__link">
Use Cases
</a>
</li>
<li class="md-tabs__item">
<a href="../../04_best_practices/" class="md-tabs__link">
Best Practices
</a>
</li>
<li class="md-tabs__item">
<a href="../../06_error_handling/" class="md-tabs__link">
Error Handling
</a>
</li>
<li class="md-tabs__item">
<a href="../14_testing_guide/" class="md-tabs__link">
Testing
</a>
</li>
</ul>
</div>
</nav>
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted md-nav--integrated" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../.." title="Mongo Ops Documentation" class="md-nav__button md-logo" aria-label="Mongo Ops Documentation" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 3C7.58 3 4 4.79 4 7s3.58 4 8 4 8-1.79 8-4-3.58-4-8-4M4 9v3c0 2.21 3.58 4 8 4s8-1.79 8-4V9c0 2.21-3.58 4-8 4s-8-1.79-8-4m0 5v3c0 2.21 3.58 4 8 4s8-1.79 8-4v-3c0 2.21-3.58 4-8 4s-8-1.79-8-4"/></svg>
</a>
Mongo Ops Documentation
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../.." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../01_overview/" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../02_components/" class="md-nav__link">
<span class="md-ellipsis">
Core Components
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" checked>
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="">
<span class="md-ellipsis">
Use Cases
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
Use Cases
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4_1" >
<label class="md-nav__link" for="__nav_4_1" id="__nav_4_1_label" tabindex="">
<span class="md-ellipsis">
Getting Started
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_1_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_1">
<span class="md-nav__icon md-icon"></span>
Getting Started
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../01_basic_crud/" class="md-nav__link">
<span class="md-ellipsis">
Basic CRUD
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../02_custom_repo/" class="md-nav__link">
<span class="md-ellipsis">
Custom Repo
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../06_multi_model/" class="md-nav__link">
<span class="md-ellipsis">
Multi Model
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4_2" >
<label class="md-nav__link" for="__nav_4_2" id="__nav_4_2_label" tabindex="">
<span class="md-ellipsis">
Data & Queries
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_2">
<span class="md-nav__icon md-icon"></span>
Data & Queries
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../04_pagination/" class="md-nav__link">
<span class="md-ellipsis">
Pagination
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../05_soft_deletes/" class="md-nav__link">
<span class="md-ellipsis">
Soft Deletes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../13_index_creation/" class="md-nav__link">
<span class="md-ellipsis">
Index Creation
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4_3" >
<label class="md-nav__link" for="__nav_4_3" id="__nav_4_3_label" tabindex="">
<span class="md-ellipsis">
Caching
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_3">
<span class="md-nav__icon md-icon"></span>
Caching
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../07_caching/" class="md-nav__link">
<span class="md-ellipsis">
Caching
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../10_cache_and_population/" class="md-nav__link">
<span class="md-ellipsis">
Cache + Population
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../11_cache_lifecycle/" class="md-nav__link">
<span class="md-ellipsis">
Cache Lifecycle
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../16_cached_repository_intricacies/" class="md-nav__link">
<span class="md-ellipsis">
Cached Repo Intricacies
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_4" checked>
<label class="md-nav__link" for="__nav_4_4" id="__nav_4_4_label" tabindex="">
<span class="md-ellipsis">
Population
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_4_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_4_4">
<span class="md-nav__icon md-icon"></span>
Population
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../08_population/" class="md-nav__link">
<span class="md-ellipsis">
Document Population
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../09_advanced_population/" class="md-nav__link">
<span class="md-ellipsis">
Advanced Population
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../15_populating_repository_wiring/" class="md-nav__link">
<span class="md-ellipsis">
PopulatingRepository Internals
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
Populate & Depopulate Examples
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Populate & Depopulate Examples
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#models-rules-used-below" class="md-nav__link">
<span class="md-ellipsis">
Models &amp; rules used below
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#1-write-path-depopulate-turns-models-into-references" class="md-nav__link">
<span class="md-ellipsis">
1. Write path — depopulate() turns models into references
</span>
</a>
<nav class="md-nav" aria-label="1. Write path — depopulate() turns models into references">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#1a-scalar-fk-no-nested_rules-stored-as-objectid" class="md-nav__link">
<span class="md-ellipsis">
1a. Scalar FK, no nested_rules → stored as ObjectId
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#1b-scalar-fk-with-nested_rules-reference-lost-do-not-do-this" class="md-nav__link">
<span class="md-ellipsis">
1b. Scalar FK with nested_rules → reference lost (do not do this)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#1c-list-fk-no-nested_rules-stored-as-listobjectid-the-safe-pattern" class="md-nav__link">
<span class="md-ellipsis">
1c. List FK, no nested_rules → stored as list[ObjectId] (the safe pattern)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#1d-list-fk-with-nested_rules-stored-as-embedded-documents-do-not-do-this" class="md-nav__link">
<span class="md-ellipsis">
1d. List FK with nested_rules → stored as embedded documents (do not do this)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#2-read-path-populate-turns-references-back-into-models" class="md-nav__link">
<span class="md-ellipsis">
2. Read path — populate() turns references back into models
</span>
</a>
<nav class="md-nav" aria-label="2. Read path — populate() turns references back into models">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#2a-scalar-fk-objectid-profile-model" class="md-nav__link">
<span class="md-ellipsis">
2a. Scalar FK — ObjectId → Profile model
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#2b-list-fk-nested_rules-two-levels-deep" class="md-nav__link">
<span class="md-ellipsis">
2b. List FK + nested_rules — two levels deep
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#3-cache-round-trip-what-actually-goes-into-the-cache" class="md-nav__link">
<span class="md-ellipsis">
3. Cache round trip — what actually goes into the cache
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#4-when-to-use-which" class="md-nav__link">
<span class="md-ellipsis">
4. When to use which
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#related" class="md-nav__link">
<span class="md-ellipsis">
Related
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4_5" >
<label class="md-nav__link" for="__nav_4_5" id="__nav_4_5_label" tabindex="">
<span class="md-ellipsis">
Advanced Usage
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_5">
<span class="md-nav__icon md-icon"></span>
Advanced Usage
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../03_transactions/" class="md-nav__link">
<span class="md-ellipsis">
Transactions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../12_transaction_helper/" class="md-nav__link">
<span class="md-ellipsis">
Transaction Helper
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../18_aggregation_pipelines/" class="md-nav__link">
<span class="md-ellipsis">
Aggregation Pipelines
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../19_bulk_operations/" class="md-nav__link">
<span class="md-ellipsis">
Bulk Operations
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../../04_best_practices/" class="md-nav__link">
<span class="md-ellipsis">
Best Practices
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../06_error_handling/" class="md-nav__link">
<span class="md-ellipsis">
Error Handling
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_7" >
<label class="md-nav__link" for="__nav_7" id="__nav_7_label" tabindex="0">
<span class="md-ellipsis">
Testing
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_7_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_7">
<span class="md-nav__icon md-icon"></span>
Testing
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../14_testing_guide/" class="md-nav__link">
<span class="md-ellipsis">
Testing Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../07_testing_example/" class="md-nav__link">
<span class="md-ellipsis">
Testing Example
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="17-populate-depopulate-worked-examples">17. Populate &amp; Depopulate — Worked Examples</h1>
<p><strong>Scenario:</strong> You want to <em>see</em> what populate/depopulate actually do to your documents — field by field, at every layer (Python model → depopulated → stored in Mongo → raw read back → populated).</p>
<p>These examples all ran against the real <code>mongo_ops</code> engine (<code>mongo_ops/populate/engine.py</code>). Timestamps and <code>ObjectId</code>s are abbreviated with <code></code> for readability.</p>
<hr />
<h2 id="models-rules-used-below">Models &amp; rules used below</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>
<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>
<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-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="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-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a> <span class="n">avatar_url</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a> <span class="n">bio</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">&quot;&quot;</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="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-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">&quot;&quot;</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">Book</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">title</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></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"># nested ref, reached via nested_rules</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="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-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
<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> <span class="c1"># scalar FK — DB stores an ObjectId</span>
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></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 FK — DB stores list[ObjectId]</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">engine</span> <span class="o">=</span> <span class="n">PopulationEngine</span><span class="p">({})</span> <span class="c1"># repos keyed by collection name, see UC 15</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="c1"># A rule WITHOUT nested_rules — the write path is safe.</span>
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></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">&quot;profile&quot;</span><span class="p">,</span> <span class="n">collection_name</span><span class="o">=</span><span class="s2">&quot;profiles&quot;</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="c1"># A rule WITH nested_rules — designed for READ-side deep population.</span>
<a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a><span class="n">books_rule</span> <span class="o">=</span> <span class="n">PopulateRule</span><span class="p">(</span>
<a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a> <span class="n">field_name</span><span class="o">=</span><span class="s2">&quot;books&quot;</span><span class="p">,</span>
<a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">&quot;books&quot;</span><span class="p">,</span>
<a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a> <span class="n">nested_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">&quot;publisher&quot;</span><span class="p">,</span> <span class="n">collection_name</span><span class="o">=</span><span class="s2">&quot;publishers&quot;</span><span class="p">)],</span>
<a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a><span class="p">)</span>
</code></pre></div>
<hr />
<h2 id="1-write-path-depopulate-turns-models-into-references">1. Write path — <code>depopulate()</code> turns models into references</h2>
<p><code>engine.depopulate(document, rules)</code> walks each rule field and <strong>mutates the model in place</strong> before you <code>insert_one</code> / <code>find_one_and_update</code> it. What you get on the other side:</p>
<h3 id="1a-scalar-fk-no-nested_rules-stored-as-objectid">1a. Scalar FK, no <code>nested_rules</code> → stored as <code>ObjectId</code></h3>
<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">demo</span><span class="p">():</span>
<a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a> <span class="n">profile</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">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…04&quot;</span><span class="p">),</span> <span class="n">avatar_url</span><span class="o">=</span><span class="s2">&quot;a.png&quot;</span><span class="p">,</span> <span class="n">bio</span><span class="o">=</span><span class="s2">&quot;hi&quot;</span><span class="p">)</span>
<a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a> <span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…05&quot;</span><span class="p">),</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;Ada&quot;</span><span class="p">,</span> <span class="n">profile</span><span class="o">=</span><span class="n">profile</span><span class="p">)</span>
<a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a>
<a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a> <span class="k">await</span> <span class="n">engine</span><span class="o">.</span><span class="n">depopulate</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="p">[</span><span class="n">profile_rule</span><span class="p">])</span>
<a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a>
<a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a> <span class="k">return</span> <span class="n">user</span><span class="o">.</span><span class="n">profile</span> <span class="c1"># ObjectId(&#39;656…04&#39;) — was a Profile model</span>
</code></pre></div>
<p>What Mongo receives (via <code>model_dump</code>, minus <code>_id</code> handling):</p>
<div class="language-json highlight"><span class="filename">JSON</span><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="p">{</span><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Ada&quot;</span><span class="p">,</span><span class="w"> </span><span class="nt">&quot;profile&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">&quot;$oid&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;656…04&quot;</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span>
</code></pre></div>
<h3 id="1b-scalar-fk-with-nested_rules-reference-lost-do-not-do-this">1b. Scalar FK <strong>with</strong> <code>nested_rules</code> → reference lost (do not do this)</h3>
<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">profile_rule_deep</span> <span class="o">=</span> <span class="n">PopulateRule</span><span class="p">(</span> <span class="c1"># same field, but with nested_rules</span>
<a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a> <span class="n">field_name</span><span class="o">=</span><span class="s2">&quot;profile&quot;</span><span class="p">,</span>
<a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a> <span class="n">collection_name</span><span class="o">=</span><span class="s2">&quot;profiles&quot;</span><span class="p">,</span>
<a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a> <span class="n">nested_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">&quot;bio&quot;</span><span class="p">,</span> <span class="n">collection_name</span><span class="o">=</span><span class="s2">&quot;profiles&quot;</span><span class="p">)],</span> <span class="c1"># any child field</span>
<a id="__codelineno-3-5" name="__codelineno-3-5" href="#__codelineno-3-5"></a><span class="p">)</span>
<a id="__codelineno-3-6" name="__codelineno-3-6" href="#__codelineno-3-6"></a>
<a id="__codelineno-3-7" name="__codelineno-3-7" href="#__codelineno-3-7"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">demo</span><span class="p">():</span>
<a id="__codelineno-3-8" name="__codelineno-3-8" href="#__codelineno-3-8"></a> <span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…05&quot;</span><span class="p">),</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;Ada&quot;</span><span class="p">,</span>
<a id="__codelineno-3-9" name="__codelineno-3-9" href="#__codelineno-3-9"></a> <span class="n">profile</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">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…04&quot;</span><span class="p">),</span> <span class="n">avatar_url</span><span class="o">=</span><span class="s2">&quot;a.png&quot;</span><span class="p">))</span>
<a id="__codelineno-3-10" name="__codelineno-3-10" href="#__codelineno-3-10"></a> <span class="k">await</span> <span class="n">engine</span><span class="o">.</span><span class="n">depopulate</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="p">[</span><span class="n">profile_rule_deep</span><span class="p">])</span>
<a id="__codelineno-3-11" name="__codelineno-3-11" href="#__codelineno-3-11"></a> <span class="k">return</span> <span class="n">user</span><span class="o">.</span><span class="n">profile</span> <span class="c1"># None — reference silently dropped!</span>
</code></pre></div>
<p>Because <code>depopulate</code> recurses <em>into</em> the child but then assigns the parent field <code>None</code> (see <code>engine.py</code><code>depopulated_value</code> is never set on that branch). On save you lose the reference entirely. There is <strong>no</strong> warning — the field is just gone.</p>
<h3 id="1c-list-fk-no-nested_rules-stored-as-listobjectid-the-safe-pattern">1c. List FK, no <code>nested_rules</code> → stored as <code>list[ObjectId]</code> (the safe pattern)</h3>
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">demo</span><span class="p">():</span>
<a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a> <span class="n">b1</span> <span class="o">=</span> <span class="n">Book</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…06&quot;</span><span class="p">),</span> <span class="n">title</span><span class="o">=</span><span class="s2">&quot;MongoDB in Action&quot;</span><span class="p">)</span>
<a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a> <span class="n">b2</span> <span class="o">=</span> <span class="n">Book</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…07&quot;</span><span class="p">),</span> <span class="n">title</span><span class="o">=</span><span class="s2">&quot;MongoDB: The Definitive Guide&quot;</span><span class="p">)</span>
<a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a> <span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…08&quot;</span><span class="p">),</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;Ada&quot;</span><span class="p">,</span> <span class="n">books</span><span class="o">=</span><span class="p">[</span><span class="n">b1</span><span class="p">,</span> <span class="n">b2</span><span class="p">])</span>
<a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a>
<a id="__codelineno-4-6" name="__codelineno-4-6" href="#__codelineno-4-6"></a> <span class="k">await</span> <span class="n">engine</span><span class="o">.</span><span class="n">depopulate</span><span class="p">(</span><span class="n">user</span><span class="p">,</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">&quot;books&quot;</span><span class="p">,</span> <span class="n">collection_name</span><span class="o">=</span><span class="s2">&quot;books&quot;</span><span class="p">)])</span>
<a id="__codelineno-4-7" name="__codelineno-4-7" href="#__codelineno-4-7"></a>
<a id="__codelineno-4-8" name="__codelineno-4-8" href="#__codelineno-4-8"></a> <span class="k">return</span> <span class="n">user</span><span class="o">.</span><span class="n">books</span> <span class="c1"># [ObjectId(&#39;656…06&#39;), ObjectId(&#39;656…07&#39;)] — clean refs</span>
</code></pre></div>
<h3 id="1d-list-fk-with-nested_rules-stored-as-embedded-documents-do-not-do-this">1d. List FK <strong>with</strong> <code>nested_rules</code> → stored as <em>embedded documents</em> (do not do this)</h3>
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">demo</span><span class="p">():</span>
<a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a> <span class="n">publisher</span> <span class="o">=</span> <span class="n">Publisher</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…09&quot;</span><span class="p">),</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;O&#39;Reilly&quot;</span><span class="p">)</span>
<a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a> <span class="n">b1p</span> <span class="o">=</span> <span class="n">Book</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…06&quot;</span><span class="p">),</span> <span class="n">title</span><span class="o">=</span><span class="s2">&quot;MongoDB in Action&quot;</span><span class="p">,</span> <span class="n">publisher</span><span class="o">=</span><span class="n">publisher</span><span class="p">)</span>
<a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a> <span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…08&quot;</span><span class="p">),</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;Ada&quot;</span><span class="p">,</span> <span class="n">books</span><span class="o">=</span><span class="p">[</span><span class="n">b1p</span><span class="p">])</span>
<a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a>
<a id="__codelineno-5-6" name="__codelineno-5-6" href="#__codelineno-5-6"></a> <span class="k">await</span> <span class="n">engine</span><span class="o">.</span><span class="n">depopulate</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="p">[</span><span class="n">books_rule</span><span class="p">])</span> <span class="c1"># books_rule carries nested_rules</span>
<a id="__codelineno-5-7" name="__codelineno-5-7" href="#__codelineno-5-7"></a> <span class="k">return</span> <span class="n">user</span><span class="o">.</span><span class="n">books</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<a id="__codelineno-5-8" name="__codelineno-5-8" href="#__codelineno-5-8"></a> <span class="c1"># &lt;Book id=ObjectId(&#39;656…06&#39;) publisher=ObjectId(&#39;656…09&#39;)&gt;</span>
</code></pre></div>
<p><code>books</code> stays a list of <strong>Book models</strong> — the nested <code>publisher</code> collapsed to <code>ObjectId</code>, but the Book itself was left embedded. Serialized to Mongo you get a nested sub-document:</p>
<div class="language-json highlight"><span class="filename">JSON</span><pre><span></span><code><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="p">{</span><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Ada&quot;</span><span class="p">,</span><span class="w"> </span><span class="nt">&quot;books&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;MongoDB in Action&quot;</span><span class="p">,</span><span class="w"> </span><span class="nt">&quot;publisher&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">&quot;$oid&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;656…09&quot;</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span>
</code></pre></div>
<p>…and <strong>reading that back</strong> through population raises <code>ValueError(…contains embedded dict(s) — run repair script…)</code>, because the engine expects <code>books</code> to hold references, not embedded docs.</p>
<blockquote>
<p><strong>Why UC 09's <code>Author → books → publisher</code> graph can't be written as-is:</strong> a rule with <code>nested_rules</code> breaks the write path (1b logs silently, 1d stores embedded). The safe round-trip: <strong>save referenced documents first</strong>, store <code>list[ObjectId]</code>/<code>ObjectId</code> with a non-nested rule for reads, and keep <code>nested_rules</code> only on rules you never hand back to <code>depopulate</code>.</p>
</blockquote>
<hr />
<h2 id="2-read-path-populate-turns-references-back-into-models">2. Read path — <code>populate()</code> turns references back into models</h2>
<p><code>PopulatingRepository.data_to_model(raw_dict)</code> resolves references <strong>before</strong> building the model. It first runs <code>_populate(dict)</code> — replacing the raw <code>ObjectId</code> references with full models via the registered repos — and only then constructs <code>self.model(**data)</code>, so every FK field is a real model by construction. The two <code>data_to_model</code> examples below (2a/2b) show the underlying resolution <code>_populate</code> performs per rule. (<code>repo</code> below is a <code>PopulatingRepository</code> wired per <a href="../15_populating_repository_wiring/">UC 15</a><code>books</code> and <code>publishers</code> repos registered in its <code>PopulationEngine</code>.)</p>
<h3 id="2a-scalar-fk-objectid-profile-model">2a. Scalar FK — <code>ObjectId</code><code>Profile</code> model</h3>
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a><span class="c1"># what came back from Mongo (raw dict, profile is an ObjectId reference):</span>
<a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a><span class="n">raw</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;_id&quot;</span><span class="p">:</span> <span class="n">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…05&quot;</span><span class="p">),</span> <span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Ada&quot;</span><span class="p">,</span> <span class="s2">&quot;profile&quot;</span><span class="p">:</span> <span class="n">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…04&quot;</span><span class="p">)}</span>
<a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a>
<a id="__codelineno-7-4" name="__codelineno-7-4" href="#__codelineno-7-4"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">demo</span><span class="p">():</span>
<a id="__codelineno-7-5" name="__codelineno-7-5" href="#__codelineno-7-5"></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">data_to_model</span><span class="p">(</span><span class="n">raw</span><span class="p">)</span> <span class="c1"># _populate() → model(**data)</span>
<a id="__codelineno-7-6" name="__codelineno-7-6" href="#__codelineno-7-6"></a> <span class="k">return</span> <span class="n">user</span><span class="o">.</span><span class="n">profile</span> <span class="c1"># Profile(id=ObjectId(&#39;656…04&#39;), avatar_url=&#39;a.png&#39;)</span>
</code></pre></div>
<h3 id="2b-list-fk-nested_rules-two-levels-deep">2b. List FK + <code>nested_rules</code> — two levels deep</h3>
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="n">raw</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;_id&quot;</span><span class="p">:</span> <span class="n">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…08&quot;</span><span class="p">),</span> <span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Ada&quot;</span><span class="p">,</span>
<a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a> <span class="s2">&quot;books&quot;</span><span class="p">:</span> <span class="p">[</span><span class="n">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…06&quot;</span><span class="p">),</span> <span class="n">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…07&quot;</span><span class="p">)]}</span>
<a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a>
<a id="__codelineno-8-4" name="__codelineno-8-4" href="#__codelineno-8-4"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">demo</span><span class="p">():</span>
<a id="__codelineno-8-5" name="__codelineno-8-5" href="#__codelineno-8-5"></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">data_to_model</span><span class="p">(</span><span class="n">raw</span><span class="p">)</span> <span class="c1"># _populate() resolves both levels</span>
<a id="__codelineno-8-6" name="__codelineno-8-6" href="#__codelineno-8-6"></a> <span class="k">return</span> <span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">books</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="c1"># &#39;Book&#39;</span>
<a id="__codelineno-8-7" name="__codelineno-8-7" href="#__codelineno-8-7"></a> <span class="nb">type</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">books</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">publisher</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="c1"># &#39;Publisher&#39; — nested_rules reached it</span>
<a id="__codelineno-8-8" name="__codelineno-8-8" href="#__codelineno-8-8"></a> <span class="n">user</span><span class="o">.</span><span class="n">books</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">publisher</span><span class="o">.</span><span class="n">name</span><span class="p">)</span> <span class="c1"># &quot;O&#39;Reilly&quot;</span>
</code></pre></div>
<p>Walking through one book's resolution:</p>
<div class="language-text highlight"><span class="filename">Text Only</span><pre><span></span><code><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a>books_rule → field &quot;books&quot;, collection &quot;books&quot;
<a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a> nested_rules[0].publisher rule → field &quot;publisher&quot;, collection &quot;publishers&quot;
<a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a>1. for ObjectId(&#39;656…06&#39;) → repo(&quot;books&quot;).get_by_id(...) → Book(&quot;MongoDB in Action&quot;)
<a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a>2. book has nested rule → repo(&quot;publishers&quot;).get_by_id(...) → Publisher(&quot;O&#39;Reilly&quot;)
<a id="__codelineno-9-5" name="__codelineno-9-5" href="#__codelineno-9-5"></a>3. author.books[0] → Book(publisher=Publisher(&quot;O&#39;Reilly&quot;))
</code></pre></div>
<p>Per-level cost: <strong>1 query per reference</strong> (no <code>$lookup</code>). A hub with 20 hooks and a <code>publisher</code> nested rule is 40 queries — that is why the next section exists.</p>
<hr />
<h2 id="3-cache-round-trip-what-actually-goes-into-the-cache">3. Cache round trip — what actually goes into the cache</h2>
<p><code>CachedBaseRepository</code> stores <code>model_dump(by_alias=True)</code> JSON-encoded with <code>default=str</code> (the <code>encode_value</code> helper in <code>mongo_ops/cache/in_memory.py</code>). Crucially it snapshots the <strong>raw, just-out-of-Mongo shape</strong> — FK fields still hold <code>ObjectId</code>s — so the cache holds references, exactly like the DB row:</p>
<table>
<thead>
<tr>
<th>Layer</th>
<th><code>profile</code> field looks like</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mongo doc (raw)</td>
<td><code>“profile”: ObjectId("656…04")</code></td>
</tr>
<tr>
<td><code>model_dump(by_alias=True)</code></td>
<td><code>"profile": ObjectId("656…04")</code></td>
</tr>
<tr>
<td><code>encode_value(...)</code></td>
<td><code>"...\"profile\": \"656…04\"..."</code> — hex string</td>
</tr>
<tr>
<td><code>decode_value(...)</code></td>
<td><code>"profile": "656…04"</code> — still a hex string</td>
</tr>
</tbody>
</table>
<p>Verbatim (timestamps abbreviated):</p>
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">mongo_ops.cache.in_memory</span><span class="w"> </span><span class="kn">import</span> <span class="n">decode_value</span><span class="p">,</span> <span class="n">encode_value</span>
<a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a>
<a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a><span class="n">snapshot</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="s2">&quot;656…05&quot;</span><span class="p">,</span> <span class="s2">&quot;profile&quot;</span><span class="p">:</span> <span class="n">ObjectId</span><span class="p">(</span><span class="s2">&quot;656…04&quot;</span><span class="p">)}</span> <span class="c1"># what a cached raw doc looks like</span>
<a id="__codelineno-10-4" name="__codelineno-10-4" href="#__codelineno-10-4"></a>
<a id="__codelineno-10-5" name="__codelineno-10-5" href="#__codelineno-10-5"></a><span class="n">cached</span> <span class="o">=</span> <span class="n">encode_value</span><span class="p">(</span><span class="n">snapshot</span><span class="p">)</span> <span class="c1"># json.dumps(snapshot, default=str).encode()</span>
<a id="__codelineno-10-6" name="__codelineno-10-6" href="#__codelineno-10-6"></a><span class="n">decoded</span> <span class="o">=</span> <span class="n">decode_value</span><span class="p">(</span><span class="n">cached</span><span class="p">)</span>
<a id="__codelineno-10-7" name="__codelineno-10-7" href="#__codelineno-10-7"></a>
<a id="__codelineno-10-8" name="__codelineno-10-8" href="#__codelineno-10-8"></a><span class="n">decoded</span><span class="p">[</span><span class="s2">&quot;profile&quot;</span><span class="p">]</span> <span class="c1"># &#39;656…04&#39; — hex STRING, not ObjectId, not Profile</span>
</code></pre></div>
<blockquote>
<p><strong>Typed-FK caveat:</strong> the round trip above only works when the FK field's model type <em>accepts</em> the raw value (<code>str</code>/<code>ObjectId</code> typed). A field typed <code>Profile | None</code> cannot even be materialized from the raw Mongo doc — both hit and miss raise <code>ValidationError</code> (UC 16's intricacy). That is exactly why UC 10's composed repo overrides <code>get_by_id</code>/<code>create</code>/<code>update</code> to carry raw data through the cache and resolve refs with <code>_populate</code>.</p>
</blockquote>
<p>Two consequences (the "intricacies", detailed in UC 16):</p>
<ol>
<li><strong>On a cache hit</strong>, <code>decode_value</code> gives you <em>hex-string references</em>. A field typed <code>Profile | None</code> receiving a hex string raises Pydantic <code>ValidationError</code> — so a plain <code>CachedBaseRepository</code> <strong>cannot</strong> serve model-typed FK fields, hit or miss. The composed pattern in UC 10 re-resolves them in its <code>get_by_id</code>: <code>data = decode_value(await cache.get(key))</code>, then <code>populated = await self._populate(data)</code>, then <code>self.model(**populated)</code>.</li>
<li><strong>On a miss</strong>, <code>BaseRepository.get_by_id</code> reads the raw Mongo doc whose <code>profile</code> is an <code>ObjectId</code> and immediately raises the same <code>ValidationError</code> — which is why UC 10 overrides <code>get_by_id</code> to fetch raw, cache raw, <em>then</em> populate. (Real ObjectIds are covered by UC 16's table.)</li>
</ol>
<hr />
<h2 id="4-when-to-use-which">4. When to use which</h2>
<table>
<thead>
<tr>
<th>You need…</th>
<th>Use</th>
</tr>
</thead>
<tbody>
<tr>
<td>Scalar <code>ObjectId</code> store, model-struct reads</td>
<td><code>PopulatingRepository</code> (UC 08)</td>
</tr>
<tr>
<td>Deep nested reads (2+ levels)</td>
<td><code>PopulatingRepository</code> + <code>nested_rules</code> (UC 09) — read side only</td>
</tr>
<tr>
<td>Same shape, but cache-first reads</td>
<td>UC 10 composed repo (engine + cache + <code>_populate</code>)</td>
</tr>
<tr>
<td>Scalar docs, no refs, hot <code>get_by_id</code></td>
<td><code>CachedBaseRepository</code> (UC 07, UC 16)</td>
</tr>
<tr>
<td><strong>Writing</strong> a reference graph back</td>
<td>depopulate <strong>without</strong> <code>nested_rules</code> — save children first, then point at their ids</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="related">Related</h2>
<ul>
<li><a href="../08_population/">08 Population</a> · <a href="../09_advanced_population/">09 Advanced population</a> · <a href="../10_cache_and_population/">10 Cache + Population</a> · <a href="../15_populating_repository_wiring/">15 PopulatingRepository wiring</a> · <a href="../16_cached_repository_intricacies/">16 CachedRepository intricacies</a></li>
</ul>
</article>
</div>
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<nav class="md-footer__inner md-grid" aria-label="Footer" >
<a href="../15_populating_repository_wiring/" class="md-footer__link md-footer__link--prev" aria-label="Previous: PopulatingRepository Internals">
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</div>
<div class="md-footer__title">
<span class="md-footer__direction">
Previous
</span>
<div class="md-ellipsis">
PopulatingRepository Internals
</div>
</div>
</a>
<a href="../03_transactions/" class="md-footer__link md-footer__link--next" aria-label="Next: Transactions">
<div class="md-footer__title">
<span class="md-footer__direction">
Next
</span>
<div class="md-ellipsis">
Transactions
</div>
</div>
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11z"/></svg>
</div>
</a>
</nav>
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"base": "../..", "features": ["navigation.tabs", "navigation.expand", "navigation.sections", "navigation.footer", "navigation.top", "navigation.instant", "toc.integrate", "content.code.annotate", "content.code.copy", "content.code.select", "content.code.line_numbers", "content.tabs.link", "content.tooltips", "content.action.edit", "search.suggest", "search.highlight", "search.share", "header.autohide", "announce.dismiss", "footer.social"], "search": "../../assets/javascripts/workers/search.973d3a69.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
<script src="../../assets/javascripts/bundle.f55a23d4.min.js"></script>
</body>
</html>