mirror of
https://github.com/d3vyce/fastapi-toolsets.git
synced 2026-08-04 15:44:09 +00:00
Deployed 06d49d5 to v5.0 with Zensical 0.0.43 and mike 2.2.0+zensical-0.1.0
This commit is contained in:
+108
-92
@@ -2102,6 +2102,22 @@
|
||||
</span><span id="__span-0-13"><a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> <span class="o">...</span>
|
||||
</span></code></pre></div>
|
||||
<p>The <code>Database</code> instance <strong>is</strong> the dependency: use it directly as <code>Depends(db)</code>. The whole request runs as a single transaction (CRUD writes use savepoints under it).</p>
|
||||
<p>The <strong>URL</strong> may be a plain string or a Pydantic <a href="https://docs.pydantic.dev/latest/api/networks/#pydantic.networks.PostgresDsn"><code>PostgresDsn</code></a>. In URL mode you can tune the engine: pass <code>connect_args</code> for DBAPI-level options and any other keyword for <code>create_async_engine</code> (e.g. <code>pool_size</code>, <code>echo</code>, <code>pool_pre_ping</code>):</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-1-1"><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">pydantic_settings</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseSettings</span>
|
||||
</span><span id="__span-1-2"><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">pydantic</span><span class="w"> </span><span class="kn">import</span> <span class="n">PostgresDsn</span>
|
||||
</span><span id="__span-1-3"><a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a>
|
||||
</span><span id="__span-1-4"><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">Settings</span><span class="p">(</span><span class="n">BaseSettings</span><span class="p">):</span>
|
||||
</span><span id="__span-1-5"><a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a> <span class="n">database_url</span><span class="p">:</span> <span class="n">PostgresDsn</span>
|
||||
</span><span id="__span-1-6"><a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a>
|
||||
</span><span id="__span-1-7"><a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a><span class="n">settings</span> <span class="o">=</span> <span class="n">Settings</span><span class="p">()</span>
|
||||
</span><span id="__span-1-8"><a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></a>
|
||||
</span><span id="__span-1-9"><a id="__codelineno-1-9" name="__codelineno-1-9" href="#__codelineno-1-9"></a><span class="n">db</span> <span class="o">=</span> <span class="n">Database</span><span class="p">(</span>
|
||||
</span><span id="__span-1-10"><a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a> <span class="n">settings</span><span class="o">.</span><span class="n">database_url</span><span class="p">,</span>
|
||||
</span><span id="__span-1-11"><a id="__codelineno-1-11" name="__codelineno-1-11" href="#__codelineno-1-11"></a> <span class="n">pool_size</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span>
|
||||
</span><span id="__span-1-12"><a id="__codelineno-1-12" name="__codelineno-1-12" href="#__codelineno-1-12"></a> <span class="n">pool_pre_ping</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
</span><span id="__span-1-13"><a id="__codelineno-1-13" name="__codelineno-1-13" href="#__codelineno-1-13"></a> <span class="n">connect_args</span><span class="o">=</span><span class="p">{</span><span class="s2">"server_settings"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"application_name"</span><span class="p">:</span> <span class="s2">"myapp"</span><span class="p">}},</span>
|
||||
</span><span id="__span-1-14"><a id="__codelineno-1-14" name="__codelineno-1-14" href="#__codelineno-1-14"></a><span class="p">)</span>
|
||||
</span></code></pre></div>
|
||||
<h2 id="committing-before-the-response">Committing before the response<a class="headerlink" href="#committing-before-the-response" title="Permanent link">¶</a></h2>
|
||||
<p><a href="../../reference/db/#fastapi_toolsets.db.Database"><code>db.install(app)</code></a> adds a middleware that commits the request's session when the response starts, after the endpoint returns and before the body is sent. With the middleware installed, the dependency does not commit again.</p>
|
||||
<p>The request is committed as a single transaction:</p>
|
||||
@@ -2117,75 +2133,75 @@
|
||||
</div>
|
||||
<h2 id="lifespan">Lifespan<a class="headerlink" href="#lifespan" title="Permanent link">¶</a></h2>
|
||||
<p><code>db.install(app)</code> disposes the engine on shutdown, composing around your own lifespan:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-1-1"><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">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
||||
</span><span id="__span-1-2"><a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a>
|
||||
</span><span id="__span-1-3"><a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a><span class="nd">@asynccontextmanager</span>
|
||||
</span><span id="__span-1-4"><a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></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><span id="__span-1-5"><a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a> <span class="k">await</span> <span class="n">warm_cache</span><span class="p">()</span> <span class="c1"># your startup</span>
|
||||
</span><span id="__span-1-6"><a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a> <span class="k">yield</span>
|
||||
</span><span id="__span-1-7"><a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a> <span class="k">await</span> <span class="n">flush_metrics</span><span class="p">()</span> <span class="c1"># your shutdown</span>
|
||||
</span><span id="__span-1-8"><a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></a>
|
||||
</span><span id="__span-1-9"><a id="__codelineno-1-9" name="__codelineno-1-9" href="#__codelineno-1-9"></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>
|
||||
</span><span id="__span-1-10"><a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a><span class="n">db</span><span class="o">.</span><span class="n">install</span><span class="p">(</span><span class="n">app</span><span class="p">)</span> <span class="c1"># your shutdown runs first, then the engine is disposed</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-2-1"><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">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
|
||||
</span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a>
|
||||
</span><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a><span class="nd">@asynccontextmanager</span>
|
||||
</span><span id="__span-2-4"><a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></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><span id="__span-2-5"><a id="__codelineno-2-5" name="__codelineno-2-5" href="#__codelineno-2-5"></a> <span class="k">await</span> <span class="n">warm_cache</span><span class="p">()</span> <span class="c1"># your startup</span>
|
||||
</span><span id="__span-2-6"><a id="__codelineno-2-6" name="__codelineno-2-6" href="#__codelineno-2-6"></a> <span class="k">yield</span>
|
||||
</span><span id="__span-2-7"><a id="__codelineno-2-7" name="__codelineno-2-7" href="#__codelineno-2-7"></a> <span class="k">await</span> <span class="n">flush_metrics</span><span class="p">()</span> <span class="c1"># your shutdown</span>
|
||||
</span><span id="__span-2-8"><a id="__codelineno-2-8" name="__codelineno-2-8" href="#__codelineno-2-8"></a>
|
||||
</span><span id="__span-2-9"><a id="__codelineno-2-9" name="__codelineno-2-9" href="#__codelineno-2-9"></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>
|
||||
</span><span id="__span-2-10"><a id="__codelineno-2-10" name="__codelineno-2-10" href="#__codelineno-2-10"></a><span class="n">db</span><span class="o">.</span><span class="n">install</span><span class="p">(</span><span class="n">app</span><span class="p">)</span> <span class="c1"># your shutdown runs first, then the engine is disposed</span>
|
||||
</span></code></pre></div>
|
||||
<p>If you have no lifespan of your own, <a href="../../reference/db/#fastapi_toolsets.db.Database"><code>db.lifespan</code></a> works standalone as <code>FastAPI(lifespan=db.lifespan)</code>. Engine disposal is idempotent and is a no-op when you passed your own <code>engine=</code>.</p>
|
||||
<h2 id="session-context-manager">Session context manager<a class="headerlink" href="#session-context-manager" title="Permanent link">¶</a></h2>
|
||||
<p>Use <a href="../../reference/db/#fastapi_toolsets.db.Database"><code>db.session()</code></a> for sessions outside request handlers (e.g. background tasks, CLI commands). It commits on clean exit and rolls back on exception:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-2-1"><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">seed</span><span class="p">():</span>
|
||||
</span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="p">()</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||||
</span><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a> <span class="o">...</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">seed</span><span class="p">():</span>
|
||||
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="p">()</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||||
</span><span id="__span-3-3"><a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a> <span class="o">...</span>
|
||||
</span></code></pre></div>
|
||||
<h2 id="transactions">Transactions<a class="headerlink" href="#transactions" title="Permanent link">¶</a></h2>
|
||||
<p><a href="../../reference/db/#fastapi_toolsets.db.transaction"><code>transaction</code></a> opens a transaction on a session, using a savepoint when one is already open so it nests safely:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">transaction</span>
|
||||
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a>
|
||||
</span><span id="__span-3-3"><a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_user_with_role</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||
</span><span id="__span-3-4"><a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">transaction</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||
</span><span id="__span-3-5"><a id="__codelineno-3-5" name="__codelineno-3-5" href="#__codelineno-3-5"></a> <span class="o">...</span>
|
||||
</span><span id="__span-3-6"><a id="__codelineno-3-6" name="__codelineno-3-6" href="#__codelineno-3-6"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">transaction</span><span class="p">(</span><span class="n">session</span><span class="p">):</span> <span class="c1"># uses a savepoint</span>
|
||||
</span><span id="__span-3-7"><a id="__codelineno-3-7" name="__codelineno-3-7" href="#__codelineno-3-7"></a> <span class="o">...</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">transaction</span>
|
||||
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a>
|
||||
</span><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_user_with_role</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||
</span><span id="__span-4-4"><a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">transaction</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||
</span><span id="__span-4-5"><a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a> <span class="o">...</span>
|
||||
</span><span id="__span-4-6"><a id="__codelineno-4-6" name="__codelineno-4-6" href="#__codelineno-4-6"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">transaction</span><span class="p">(</span><span class="n">session</span><span class="p">):</span> <span class="c1"># uses a savepoint</span>
|
||||
</span><span id="__span-4-7"><a id="__codelineno-4-7" name="__codelineno-4-7" href="#__codelineno-4-7"></a> <span class="o">...</span>
|
||||
</span></code></pre></div>
|
||||
<p>When you have a <code>Database</code>, <a href="../../reference/db/#fastapi_toolsets.db.Database"><code>db.begin()</code></a> opens a session already inside a transaction:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="k">async</span> <span class="k">with</span> <span class="n">db</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||||
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a> <span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">User</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">"ada"</span><span class="p">))</span> <span class="c1"># commits on exit, rolls back on exception</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="k">async</span> <span class="k">with</span> <span class="n">db</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||||
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a> <span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">User</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">"ada"</span><span class="p">))</span> <span class="c1"># commits on exit, rolls back on exception</span>
|
||||
</span></code></pre></div>
|
||||
<h2 id="table-locking">Table locking<a class="headerlink" href="#table-locking" title="Permanent link">¶</a></h2>
|
||||
<p><a href="../../reference/db/#fastapi_toolsets.db.Database"><code>db.lock_tables</code></a> acquires PostgreSQL table-level locks for a critical section. It opens a dedicated session internally and releases the lock when the context exits:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">LockMode</span>
|
||||
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a>
|
||||
</span><span id="__span-5-3"><a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a><span class="k">async</span> <span class="k">with</span> <span class="n">db</span><span class="o">.</span><span class="n">lock_tables</span><span class="p">([</span><span class="n">User</span><span class="p">],</span> <span class="n">mode</span><span class="o">=</span><span class="n">LockMode</span><span class="o">.</span><span class="n">EXCLUSIVE</span><span class="p">)</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||||
</span><span id="__span-5-4"><a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a> <span class="c1"># No other transaction can modify User until this block exits</span>
|
||||
</span><span id="__span-5-5"><a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a> <span class="o">...</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-6-1"><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">LockMode</span>
|
||||
</span><span id="__span-6-2"><a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a>
|
||||
</span><span id="__span-6-3"><a id="__codelineno-6-3" name="__codelineno-6-3" href="#__codelineno-6-3"></a><span class="k">async</span> <span class="k">with</span> <span class="n">db</span><span class="o">.</span><span class="n">lock_tables</span><span class="p">([</span><span class="n">User</span><span class="p">],</span> <span class="n">mode</span><span class="o">=</span><span class="n">LockMode</span><span class="o">.</span><span class="n">EXCLUSIVE</span><span class="p">)</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||||
</span><span id="__span-6-4"><a id="__codelineno-6-4" name="__codelineno-6-4" href="#__codelineno-6-4"></a> <span class="c1"># No other transaction can modify User until this block exits</span>
|
||||
</span><span id="__span-6-5"><a id="__codelineno-6-5" name="__codelineno-6-5" href="#__codelineno-6-5"></a> <span class="o">...</span>
|
||||
</span></code></pre></div>
|
||||
<p>Available lock modes are defined in <a href="../../reference/db/#fastapi_toolsets.db.LockMode"><code>LockMode</code></a>: <code>ACCESS_SHARE</code>, <code>ROW_SHARE</code>, <code>ROW_EXCLUSIVE</code>, <code>SHARE_UPDATE_EXCLUSIVE</code>, <code>SHARE</code>, <code>SHARE_ROW_EXCLUSIVE</code>, <code>EXCLUSIVE</code>, <code>ACCESS_EXCLUSIVE</code>.</p>
|
||||
<p>Pass <code>timeout</code> to limit how long the lock waits. On timeout, a <a href="../../reference/exceptions/#fastapi_toolsets.exceptions.exceptions.LockTimeoutError"><code>LockTimeoutError</code></a> is raised instead of a raw database error:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-6-1"><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="k">async</span> <span class="k">with</span> <span class="n">db</span><span class="o">.</span><span class="n">lock_tables</span><span class="p">([</span><span class="n">Order</span><span class="p">],</span> <span class="n">timeout</span><span class="o">=</span><span class="s2">"2s"</span><span class="p">)</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||||
</span><span id="__span-6-2"><a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a> <span class="o">...</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a><span class="k">async</span> <span class="k">with</span> <span class="n">db</span><span class="o">.</span><span class="n">lock_tables</span><span class="p">([</span><span class="n">Order</span><span class="p">],</span> <span class="n">timeout</span><span class="o">=</span><span class="s2">"2s"</span><span class="p">)</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||||
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a> <span class="o">...</span>
|
||||
</span></code></pre></div>
|
||||
<h2 id="advisory-locking">Advisory locking<a class="headerlink" href="#advisory-locking" title="Permanent link">¶</a></h2>
|
||||
<p><a href="../../reference/db/#fastapi_toolsets.db.advisory_lock"><code>advisory_lock</code></a> acquires a PostgreSQL session-level advisory lock on a session you provide. The lock is released when the context exits:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">advisory_lock</span>
|
||||
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a>
|
||||
</span><span id="__span-7-3"><a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a><span class="c1"># Blocking exclusive lock: waits until the lock is free</span>
|
||||
</span><span id="__span-7-4"><a id="__codelineno-7-4" name="__codelineno-7-4" href="#__codelineno-7-4"></a><span class="k">async</span> <span class="k">with</span> <span class="n">advisory_lock</span><span class="p">(</span><span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="mi">42</span><span class="p">):</span>
|
||||
</span><span id="__span-7-5"><a id="__codelineno-7-5" name="__codelineno-7-5" href="#__codelineno-7-5"></a> <span class="o">...</span>
|
||||
</span><span id="__span-7-6"><a id="__codelineno-7-6" name="__codelineno-7-6" href="#__codelineno-7-6"></a>
|
||||
</span><span id="__span-7-7"><a id="__codelineno-7-7" name="__codelineno-7-7" href="#__codelineno-7-7"></a><span class="c1"># Non-blocking: yields False immediately if already held</span>
|
||||
</span><span id="__span-7-8"><a id="__codelineno-7-8" name="__codelineno-7-8" href="#__codelineno-7-8"></a><span class="k">async</span> <span class="k">with</span> <span class="n">advisory_lock</span><span class="p">(</span><span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="mi">42</span><span class="p">,</span> <span class="n">nowait</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="k">as</span> <span class="n">acquired</span><span class="p">:</span>
|
||||
</span><span id="__span-7-9"><a id="__codelineno-7-9" name="__codelineno-7-9" href="#__codelineno-7-9"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">acquired</span><span class="p">:</span>
|
||||
</span><span id="__span-7-10"><a id="__codelineno-7-10" name="__codelineno-7-10" href="#__codelineno-7-10"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="mi">409</span><span class="p">,</span> <span class="s2">"Resource is locked"</span><span class="p">)</span>
|
||||
</span><span id="__span-7-11"><a id="__codelineno-7-11" name="__codelineno-7-11" href="#__codelineno-7-11"></a>
|
||||
</span><span id="__span-7-12"><a id="__codelineno-7-12" name="__codelineno-7-12" href="#__codelineno-7-12"></a><span class="c1"># Blocking with a timeout: raises LockTimeoutError if not acquired in time</span>
|
||||
</span><span id="__span-7-13"><a id="__codelineno-7-13" name="__codelineno-7-13" href="#__codelineno-7-13"></a><span class="k">async</span> <span class="k">with</span> <span class="n">advisory_lock</span><span class="p">(</span><span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="mi">42</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="s2">"5s"</span><span class="p">):</span>
|
||||
</span><span id="__span-7-14"><a id="__codelineno-7-14" name="__codelineno-7-14" href="#__codelineno-7-14"></a> <span class="o">...</span>
|
||||
</span><span id="__span-7-15"><a id="__codelineno-7-15" name="__codelineno-7-15" href="#__codelineno-7-15"></a>
|
||||
</span><span id="__span-7-16"><a id="__codelineno-7-16" name="__codelineno-7-16" href="#__codelineno-7-16"></a><span class="c1"># Shared lock: multiple readers allowed simultaneously, blocks exclusive writers</span>
|
||||
</span><span id="__span-7-17"><a id="__codelineno-7-17" name="__codelineno-7-17" href="#__codelineno-7-17"></a><span class="k">async</span> <span class="k">with</span> <span class="n">advisory_lock</span><span class="p">(</span><span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="mi">42</span><span class="p">,</span> <span class="n">shared</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
|
||||
</span><span id="__span-7-18"><a id="__codelineno-7-18" name="__codelineno-7-18" href="#__codelineno-7-18"></a> <span class="o">...</span>
|
||||
</span><span id="__span-7-19"><a id="__codelineno-7-19" name="__codelineno-7-19" href="#__codelineno-7-19"></a>
|
||||
</span><span id="__span-7-20"><a id="__codelineno-7-20" name="__codelineno-7-20" href="#__codelineno-7-20"></a><span class="c1"># Two-integer key for namespacing (e.g. lock_type + resource_id)</span>
|
||||
</span><span id="__span-7-21"><a id="__codelineno-7-21" name="__codelineno-7-21" href="#__codelineno-7-21"></a><span class="k">async</span> <span class="k">with</span> <span class="n">advisory_lock</span><span class="p">(</span><span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">user_id</span><span class="p">)):</span>
|
||||
</span><span id="__span-7-22"><a id="__codelineno-7-22" name="__codelineno-7-22" href="#__codelineno-7-22"></a> <span class="o">...</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-8-1"><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">advisory_lock</span>
|
||||
</span><span id="__span-8-2"><a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a>
|
||||
</span><span id="__span-8-3"><a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a><span class="c1"># Blocking exclusive lock: waits until the lock is free</span>
|
||||
</span><span id="__span-8-4"><a id="__codelineno-8-4" name="__codelineno-8-4" href="#__codelineno-8-4"></a><span class="k">async</span> <span class="k">with</span> <span class="n">advisory_lock</span><span class="p">(</span><span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="mi">42</span><span class="p">):</span>
|
||||
</span><span id="__span-8-5"><a id="__codelineno-8-5" name="__codelineno-8-5" href="#__codelineno-8-5"></a> <span class="o">...</span>
|
||||
</span><span id="__span-8-6"><a id="__codelineno-8-6" name="__codelineno-8-6" href="#__codelineno-8-6"></a>
|
||||
</span><span id="__span-8-7"><a id="__codelineno-8-7" name="__codelineno-8-7" href="#__codelineno-8-7"></a><span class="c1"># Non-blocking: yields False immediately if already held</span>
|
||||
</span><span id="__span-8-8"><a id="__codelineno-8-8" name="__codelineno-8-8" href="#__codelineno-8-8"></a><span class="k">async</span> <span class="k">with</span> <span class="n">advisory_lock</span><span class="p">(</span><span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="mi">42</span><span class="p">,</span> <span class="n">nowait</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="k">as</span> <span class="n">acquired</span><span class="p">:</span>
|
||||
</span><span id="__span-8-9"><a id="__codelineno-8-9" name="__codelineno-8-9" href="#__codelineno-8-9"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">acquired</span><span class="p">:</span>
|
||||
</span><span id="__span-8-10"><a id="__codelineno-8-10" name="__codelineno-8-10" href="#__codelineno-8-10"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="mi">409</span><span class="p">,</span> <span class="s2">"Resource is locked"</span><span class="p">)</span>
|
||||
</span><span id="__span-8-11"><a id="__codelineno-8-11" name="__codelineno-8-11" href="#__codelineno-8-11"></a>
|
||||
</span><span id="__span-8-12"><a id="__codelineno-8-12" name="__codelineno-8-12" href="#__codelineno-8-12"></a><span class="c1"># Blocking with a timeout: raises LockTimeoutError if not acquired in time</span>
|
||||
</span><span id="__span-8-13"><a id="__codelineno-8-13" name="__codelineno-8-13" href="#__codelineno-8-13"></a><span class="k">async</span> <span class="k">with</span> <span class="n">advisory_lock</span><span class="p">(</span><span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="mi">42</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="s2">"5s"</span><span class="p">):</span>
|
||||
</span><span id="__span-8-14"><a id="__codelineno-8-14" name="__codelineno-8-14" href="#__codelineno-8-14"></a> <span class="o">...</span>
|
||||
</span><span id="__span-8-15"><a id="__codelineno-8-15" name="__codelineno-8-15" href="#__codelineno-8-15"></a>
|
||||
</span><span id="__span-8-16"><a id="__codelineno-8-16" name="__codelineno-8-16" href="#__codelineno-8-16"></a><span class="c1"># Shared lock: multiple readers allowed simultaneously, blocks exclusive writers</span>
|
||||
</span><span id="__span-8-17"><a id="__codelineno-8-17" name="__codelineno-8-17" href="#__codelineno-8-17"></a><span class="k">async</span> <span class="k">with</span> <span class="n">advisory_lock</span><span class="p">(</span><span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="mi">42</span><span class="p">,</span> <span class="n">shared</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
|
||||
</span><span id="__span-8-18"><a id="__codelineno-8-18" name="__codelineno-8-18" href="#__codelineno-8-18"></a> <span class="o">...</span>
|
||||
</span><span id="__span-8-19"><a id="__codelineno-8-19" name="__codelineno-8-19" href="#__codelineno-8-19"></a>
|
||||
</span><span id="__span-8-20"><a id="__codelineno-8-20" name="__codelineno-8-20" href="#__codelineno-8-20"></a><span class="c1"># Two-integer key for namespacing (e.g. lock_type + resource_id)</span>
|
||||
</span><span id="__span-8-21"><a id="__codelineno-8-21" name="__codelineno-8-21" href="#__codelineno-8-21"></a><span class="k">async</span> <span class="k">with</span> <span class="n">advisory_lock</span><span class="p">(</span><span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">user_id</span><span class="p">)):</span>
|
||||
</span><span id="__span-8-22"><a id="__codelineno-8-22" name="__codelineno-8-22" href="#__codelineno-8-22"></a> <span class="o">...</span>
|
||||
</span></code></pre></div>
|
||||
<div class="admonition note">
|
||||
<p class="admonition-title">Note</p>
|
||||
@@ -2193,67 +2209,67 @@
|
||||
</div>
|
||||
<h2 id="row-change-polling">Row-change polling<a class="headerlink" href="#row-change-polling" title="Permanent link">¶</a></h2>
|
||||
<p><a href="../../reference/db/#fastapi_toolsets.db.wait_for_row_change"><code>wait_for_row_change</code></a> polls a row until a specific column changes value:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-8-1"><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">wait_for_row_change</span>
|
||||
</span><span id="__span-8-2"><a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a>
|
||||
</span><span id="__span-8-3"><a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a><span class="c1"># Wait up to 30s for order.status to change</span>
|
||||
</span><span id="__span-8-4"><a id="__codelineno-8-4" name="__codelineno-8-4" href="#__codelineno-8-4"></a><span class="k">await</span> <span class="n">wait_for_row_change</span><span class="p">(</span>
|
||||
</span><span id="__span-8-5"><a id="__codelineno-8-5" name="__codelineno-8-5" href="#__codelineno-8-5"></a> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span>
|
||||
</span><span id="__span-8-6"><a id="__codelineno-8-6" name="__codelineno-8-6" href="#__codelineno-8-6"></a> <span class="n">model</span><span class="o">=</span><span class="n">Order</span><span class="p">,</span>
|
||||
</span><span id="__span-8-7"><a id="__codelineno-8-7" name="__codelineno-8-7" href="#__codelineno-8-7"></a> <span class="n">pk_value</span><span class="o">=</span><span class="n">order_id</span><span class="p">,</span>
|
||||
</span><span id="__span-8-8"><a id="__codelineno-8-8" name="__codelineno-8-8" href="#__codelineno-8-8"></a> <span class="n">columns</span><span class="o">=</span><span class="p">[</span><span class="s2">"status"</span><span class="p">],</span>
|
||||
</span><span id="__span-8-9"><a id="__codelineno-8-9" name="__codelineno-8-9" href="#__codelineno-8-9"></a> <span class="n">interval</span><span class="o">=</span><span class="mf">1.0</span><span class="p">,</span>
|
||||
</span><span id="__span-8-10"><a id="__codelineno-8-10" name="__codelineno-8-10" href="#__codelineno-8-10"></a> <span class="n">timeout</span><span class="o">=</span><span class="mf">30.0</span><span class="p">,</span>
|
||||
</span><span id="__span-8-11"><a id="__codelineno-8-11" name="__codelineno-8-11" href="#__codelineno-8-11"></a><span class="p">)</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-9-1"><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">wait_for_row_change</span>
|
||||
</span><span id="__span-9-2"><a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a>
|
||||
</span><span id="__span-9-3"><a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a><span class="c1"># Wait up to 30s for order.status to change</span>
|
||||
</span><span id="__span-9-4"><a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a><span class="k">await</span> <span class="n">wait_for_row_change</span><span class="p">(</span>
|
||||
</span><span id="__span-9-5"><a id="__codelineno-9-5" name="__codelineno-9-5" href="#__codelineno-9-5"></a> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span>
|
||||
</span><span id="__span-9-6"><a id="__codelineno-9-6" name="__codelineno-9-6" href="#__codelineno-9-6"></a> <span class="n">model</span><span class="o">=</span><span class="n">Order</span><span class="p">,</span>
|
||||
</span><span id="__span-9-7"><a id="__codelineno-9-7" name="__codelineno-9-7" href="#__codelineno-9-7"></a> <span class="n">pk_value</span><span class="o">=</span><span class="n">order_id</span><span class="p">,</span>
|
||||
</span><span id="__span-9-8"><a id="__codelineno-9-8" name="__codelineno-9-8" href="#__codelineno-9-8"></a> <span class="n">columns</span><span class="o">=</span><span class="p">[</span><span class="s2">"status"</span><span class="p">],</span>
|
||||
</span><span id="__span-9-9"><a id="__codelineno-9-9" name="__codelineno-9-9" href="#__codelineno-9-9"></a> <span class="n">interval</span><span class="o">=</span><span class="mf">1.0</span><span class="p">,</span>
|
||||
</span><span id="__span-9-10"><a id="__codelineno-9-10" name="__codelineno-9-10" href="#__codelineno-9-10"></a> <span class="n">timeout</span><span class="o">=</span><span class="mf">30.0</span><span class="p">,</span>
|
||||
</span><span id="__span-9-11"><a id="__codelineno-9-11" name="__codelineno-9-11" href="#__codelineno-9-11"></a><span class="p">)</span>
|
||||
</span></code></pre></div>
|
||||
<h2 id="creating-a-database">Creating a database<a class="headerlink" href="#creating-a-database" title="Permanent link">¶</a></h2>
|
||||
<p><a href="../../reference/db/#fastapi_toolsets.db.testing.create_database"><code>create_database</code></a> (in <code>fastapi_toolsets.db.testing</code>) connects to <em>server_url</em> and issues a <code>CREATE DATABASE</code> statement:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-9-1"><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db.testing</span><span class="w"> </span><span class="kn">import</span> <span class="n">create_database</span>
|
||||
</span><span id="__span-9-2"><a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a>
|
||||
</span><span id="__span-9-3"><a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a><span class="n">SERVER_URL</span> <span class="o">=</span> <span class="s2">"postgresql+asyncpg://postgres:postgres@localhost/postgres"</span>
|
||||
</span><span id="__span-9-4"><a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a>
|
||||
</span><span id="__span-9-5"><a id="__codelineno-9-5" name="__codelineno-9-5" href="#__codelineno-9-5"></a><span class="k">await</span> <span class="n">create_database</span><span class="p">(</span><span class="n">db_name</span><span class="o">=</span><span class="s2">"myapp_test"</span><span class="p">,</span> <span class="n">server_url</span><span class="o">=</span><span class="n">SERVER_URL</span><span class="p">)</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-10-1"><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">fastapi_toolsets.db.testing</span><span class="w"> </span><span class="kn">import</span> <span class="n">create_database</span>
|
||||
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a>
|
||||
</span><span id="__span-10-3"><a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a><span class="n">SERVER_URL</span> <span class="o">=</span> <span class="s2">"postgresql+asyncpg://postgres:postgres@localhost/postgres"</span>
|
||||
</span><span id="__span-10-4"><a id="__codelineno-10-4" name="__codelineno-10-4" href="#__codelineno-10-4"></a>
|
||||
</span><span id="__span-10-5"><a id="__codelineno-10-5" name="__codelineno-10-5" href="#__codelineno-10-5"></a><span class="k">await</span> <span class="n">create_database</span><span class="p">(</span><span class="n">db_name</span><span class="o">=</span><span class="s2">"myapp_test"</span><span class="p">,</span> <span class="n">server_url</span><span class="o">=</span><span class="n">SERVER_URL</span><span class="p">)</span>
|
||||
</span></code></pre></div>
|
||||
<p>For test isolation with automatic cleanup, use <a href="../../reference/pytest/#fastapi_toolsets.pytest.utils.create_worker_database"><code>create_worker_database</code></a> from the <code>pytest</code> module, which handles drop-before, create, and drop-after.</p>
|
||||
<h2 id="cleaning-up-tables">Cleaning up tables<a class="headerlink" href="#cleaning-up-tables" title="Permanent link">¶</a></h2>
|
||||
<p><a href="../../reference/db/#fastapi_toolsets.db.testing.cleanup_tables"><code>cleanup_tables</code></a> (in <code>fastapi_toolsets.db.testing</code>) truncates all tables:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-10-1"><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">fastapi_toolsets.db.testing</span><span class="w"> </span><span class="kn">import</span> <span class="n">cleanup_tables</span>
|
||||
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a>
|
||||
</span><span id="__span-10-3"><a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span><span class="p">(</span><span class="n">autouse</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||||
</span><span id="__span-10-4"><a id="__codelineno-10-4" name="__codelineno-10-4" href="#__codelineno-10-4"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">clean</span><span class="p">(</span><span class="n">db_session</span><span class="p">):</span>
|
||||
</span><span id="__span-10-5"><a id="__codelineno-10-5" name="__codelineno-10-5" href="#__codelineno-10-5"></a> <span class="k">yield</span>
|
||||
</span><span id="__span-10-6"><a id="__codelineno-10-6" name="__codelineno-10-6" href="#__codelineno-10-6"></a> <span class="k">await</span> <span class="n">cleanup_tables</span><span class="p">(</span><span class="n">session</span><span class="o">=</span><span class="n">db_session</span><span class="p">,</span> <span class="n">base</span><span class="o">=</span><span class="n">Base</span><span class="p">)</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-11-1"><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db.testing</span><span class="w"> </span><span class="kn">import</span> <span class="n">cleanup_tables</span>
|
||||
</span><span id="__span-11-2"><a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a>
|
||||
</span><span id="__span-11-3"><a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span><span class="p">(</span><span class="n">autouse</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||||
</span><span id="__span-11-4"><a id="__codelineno-11-4" name="__codelineno-11-4" href="#__codelineno-11-4"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">clean</span><span class="p">(</span><span class="n">db_session</span><span class="p">):</span>
|
||||
</span><span id="__span-11-5"><a id="__codelineno-11-5" name="__codelineno-11-5" href="#__codelineno-11-5"></a> <span class="k">yield</span>
|
||||
</span><span id="__span-11-6"><a id="__codelineno-11-6" name="__codelineno-11-6" href="#__codelineno-11-6"></a> <span class="k">await</span> <span class="n">cleanup_tables</span><span class="p">(</span><span class="n">session</span><span class="o">=</span><span class="n">db_session</span><span class="p">,</span> <span class="n">base</span><span class="o">=</span><span class="n">Base</span><span class="p">)</span>
|
||||
</span></code></pre></div>
|
||||
<h2 id="many-to-many-helpers">Many-to-Many helpers<a class="headerlink" href="#many-to-many-helpers" title="Permanent link">¶</a></h2>
|
||||
<p>The three <code>m2m_*</code> helpers modify a many-to-many association table with direct SQL, without loading the ORM collection.</p>
|
||||
<h3 id="m2m_add-insert-associations"><code>m2m_add</code>: insert associations<a class="headerlink" href="#m2m_add-insert-associations" title="Permanent link">¶</a></h3>
|
||||
<p><a href="../../reference/db/#fastapi_toolsets.db.m2m_add"><code>m2m_add</code></a> inserts one or more rows into a secondary table:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-11-1"><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">m2m_add</span>
|
||||
</span><span id="__span-11-2"><a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a>
|
||||
</span><span id="__span-11-3"><a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a><span class="k">async</span> <span class="k">with</span> <span class="n">db</span><span class="o">.</span><span class="n">lock_tables</span><span class="p">([</span><span class="n">Tag</span><span class="p">])</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||||
</span><span id="__span-11-4"><a id="__codelineno-11-4" name="__codelineno-11-4" href="#__codelineno-11-4"></a> <span class="n">tag</span> <span class="o">=</span> <span class="k">await</span> <span class="n">TagCrud</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">TagCreate</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">"python"</span><span class="p">))</span>
|
||||
</span><span id="__span-11-5"><a id="__codelineno-11-5" name="__codelineno-11-5" href="#__codelineno-11-5"></a> <span class="k">await</span> <span class="n">m2m_add</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">post</span><span class="p">,</span> <span class="n">Post</span><span class="o">.</span><span class="n">tags</span><span class="p">,</span> <span class="n">tag</span><span class="p">)</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-12-1"><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">m2m_add</span>
|
||||
</span><span id="__span-12-2"><a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a>
|
||||
</span><span id="__span-12-3"><a id="__codelineno-12-3" name="__codelineno-12-3" href="#__codelineno-12-3"></a><span class="k">async</span> <span class="k">with</span> <span class="n">db</span><span class="o">.</span><span class="n">lock_tables</span><span class="p">([</span><span class="n">Tag</span><span class="p">])</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||||
</span><span id="__span-12-4"><a id="__codelineno-12-4" name="__codelineno-12-4" href="#__codelineno-12-4"></a> <span class="n">tag</span> <span class="o">=</span> <span class="k">await</span> <span class="n">TagCrud</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">TagCreate</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">"python"</span><span class="p">))</span>
|
||||
</span><span id="__span-12-5"><a id="__codelineno-12-5" name="__codelineno-12-5" href="#__codelineno-12-5"></a> <span class="k">await</span> <span class="n">m2m_add</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">post</span><span class="p">,</span> <span class="n">Post</span><span class="o">.</span><span class="n">tags</span><span class="p">,</span> <span class="n">tag</span><span class="p">)</span>
|
||||
</span></code></pre></div>
|
||||
<p>Pass <code>ignore_conflicts=True</code> to skip associations that already exist:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-12-1"><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a><span class="k">await</span> <span class="n">m2m_add</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">post</span><span class="p">,</span> <span class="n">Post</span><span class="o">.</span><span class="n">tags</span><span class="p">,</span> <span class="n">tag</span><span class="p">,</span> <span class="n">ignore_conflicts</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-13-1"><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="k">await</span> <span class="n">m2m_add</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">post</span><span class="p">,</span> <span class="n">Post</span><span class="o">.</span><span class="n">tags</span><span class="p">,</span> <span class="n">tag</span><span class="p">,</span> <span class="n">ignore_conflicts</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||||
</span></code></pre></div>
|
||||
<h3 id="m2m_remove-delete-associations"><code>m2m_remove</code>: delete associations<a class="headerlink" href="#m2m_remove-delete-associations" title="Permanent link">¶</a></h3>
|
||||
<p><a href="../../reference/db/#fastapi_toolsets.db.m2m_remove"><code>m2m_remove</code></a> deletes specific association rows. Removing a non-existent association is a no-op:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-13-1"><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">m2m_remove</span><span class="p">,</span> <span class="n">transaction</span>
|
||||
</span><span id="__span-13-2"><a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a>
|
||||
</span><span id="__span-13-3"><a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a><span class="k">async</span> <span class="k">with</span> <span class="n">transaction</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||
</span><span id="__span-13-4"><a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a> <span class="k">await</span> <span class="n">m2m_remove</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">post</span><span class="p">,</span> <span class="n">Post</span><span class="o">.</span><span class="n">tags</span><span class="p">,</span> <span class="n">tag1</span><span class="p">,</span> <span class="n">tag2</span><span class="p">)</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-14-1"><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">m2m_remove</span><span class="p">,</span> <span class="n">transaction</span>
|
||||
</span><span id="__span-14-2"><a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a>
|
||||
</span><span id="__span-14-3"><a id="__codelineno-14-3" name="__codelineno-14-3" href="#__codelineno-14-3"></a><span class="k">async</span> <span class="k">with</span> <span class="n">transaction</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||
</span><span id="__span-14-4"><a id="__codelineno-14-4" name="__codelineno-14-4" href="#__codelineno-14-4"></a> <span class="k">await</span> <span class="n">m2m_remove</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">post</span><span class="p">,</span> <span class="n">Post</span><span class="o">.</span><span class="n">tags</span><span class="p">,</span> <span class="n">tag1</span><span class="p">,</span> <span class="n">tag2</span><span class="p">)</span>
|
||||
</span></code></pre></div>
|
||||
<h3 id="m2m_set-replace-the-full-set"><code>m2m_set</code>: replace the full set<a class="headerlink" href="#m2m_set-replace-the-full-set" title="Permanent link">¶</a></h3>
|
||||
<p><a href="../../reference/db/#fastapi_toolsets.db.m2m_set"><code>m2m_set</code></a> replaces all associations: it deletes every existing row for the owner instance then inserts the new set. Passing no related instances clears the association:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-14-1"><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">m2m_set</span><span class="p">,</span> <span class="n">transaction</span>
|
||||
</span><span id="__span-14-2"><a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a>
|
||||
</span><span id="__span-14-3"><a id="__codelineno-14-3" name="__codelineno-14-3" href="#__codelineno-14-3"></a><span class="c1"># Replace all tags</span>
|
||||
</span><span id="__span-14-4"><a id="__codelineno-14-4" name="__codelineno-14-4" href="#__codelineno-14-4"></a><span class="k">async</span> <span class="k">with</span> <span class="n">transaction</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||
</span><span id="__span-14-5"><a id="__codelineno-14-5" name="__codelineno-14-5" href="#__codelineno-14-5"></a> <span class="k">await</span> <span class="n">m2m_set</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">post</span><span class="p">,</span> <span class="n">Post</span><span class="o">.</span><span class="n">tags</span><span class="p">,</span> <span class="n">tag_a</span><span class="p">,</span> <span class="n">tag_b</span><span class="p">)</span>
|
||||
</span><span id="__span-14-6"><a id="__codelineno-14-6" name="__codelineno-14-6" href="#__codelineno-14-6"></a>
|
||||
</span><span id="__span-14-7"><a id="__codelineno-14-7" name="__codelineno-14-7" href="#__codelineno-14-7"></a><span class="c1"># Clear all tags</span>
|
||||
</span><span id="__span-14-8"><a id="__codelineno-14-8" name="__codelineno-14-8" href="#__codelineno-14-8"></a><span class="k">async</span> <span class="k">with</span> <span class="n">transaction</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||
</span><span id="__span-14-9"><a id="__codelineno-14-9" name="__codelineno-14-9" href="#__codelineno-14-9"></a> <span class="k">await</span> <span class="n">m2m_set</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">post</span><span class="p">,</span> <span class="n">Post</span><span class="o">.</span><span class="n">tags</span><span class="p">)</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-15-1"><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.db</span><span class="w"> </span><span class="kn">import</span> <span class="n">m2m_set</span><span class="p">,</span> <span class="n">transaction</span>
|
||||
</span><span id="__span-15-2"><a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a>
|
||||
</span><span id="__span-15-3"><a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a><span class="c1"># Replace all tags</span>
|
||||
</span><span id="__span-15-4"><a id="__codelineno-15-4" name="__codelineno-15-4" href="#__codelineno-15-4"></a><span class="k">async</span> <span class="k">with</span> <span class="n">transaction</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||
</span><span id="__span-15-5"><a id="__codelineno-15-5" name="__codelineno-15-5" href="#__codelineno-15-5"></a> <span class="k">await</span> <span class="n">m2m_set</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">post</span><span class="p">,</span> <span class="n">Post</span><span class="o">.</span><span class="n">tags</span><span class="p">,</span> <span class="n">tag_a</span><span class="p">,</span> <span class="n">tag_b</span><span class="p">)</span>
|
||||
</span><span id="__span-15-6"><a id="__codelineno-15-6" name="__codelineno-15-6" href="#__codelineno-15-6"></a>
|
||||
</span><span id="__span-15-7"><a id="__codelineno-15-7" name="__codelineno-15-7" href="#__codelineno-15-7"></a><span class="c1"># Clear all tags</span>
|
||||
</span><span id="__span-15-8"><a id="__codelineno-15-8" name="__codelineno-15-8" href="#__codelineno-15-8"></a><span class="k">async</span> <span class="k">with</span> <span class="n">transaction</span><span class="p">(</span><span class="n">session</span><span class="p">):</span>
|
||||
</span><span id="__span-15-9"><a id="__codelineno-15-9" name="__codelineno-15-9" href="#__codelineno-15-9"></a> <span class="k">await</span> <span class="n">m2m_set</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">post</span><span class="p">,</span> <span class="n">Post</span><span class="o">.</span><span class="n">tags</span><span class="p">)</span>
|
||||
</span></code></pre></div>
|
||||
<p>All three helpers raise <code>TypeError</code> if the relationship attribute is not a Many-to-Many (i.e. has no secondary table).</p>
|
||||
<hr />
|
||||
|
||||
@@ -2078,40 +2078,45 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<p>A fixture with no <code>contexts</code> defined takes <code>Context.BASE</code> by default.</p>
|
||||
<p><code>Context.BASE</code> fixtures are always included alongside whatever context you load or list — there's no way to load a non-base context in isolation:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="c1"># also loads any Context.BASE fixtures, even though only TESTING is requested</span>
|
||||
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a><span class="k">await</span> <span class="n">load_fixtures_by_context</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">fixtures</span><span class="p">,</span> <span class="n">Context</span><span class="o">.</span><span class="n">TESTING</span><span class="p">)</span>
|
||||
</span></code></pre></div>
|
||||
<h3 id="custom-contexts">Custom contexts<a class="headerlink" href="#custom-contexts" title="Permanent link">¶</a></h3>
|
||||
<p>Plain strings and any <code>Enum</code> subclass are accepted wherever a <code>Context</code> enum is expected.</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">enum</span><span class="w"> </span><span class="kn">import</span> <span class="n">Enum</span>
|
||||
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a>
|
||||
</span><span id="__span-3-3"><a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a><span class="k">class</span><span class="w"> </span><span class="nc">AppContext</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="n">Enum</span><span class="p">):</span>
|
||||
</span><span id="__span-3-4"><a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a> <span class="n">STAGING</span> <span class="o">=</span> <span class="s2">"staging"</span>
|
||||
</span><span id="__span-3-5"><a id="__codelineno-3-5" name="__codelineno-3-5" href="#__codelineno-3-5"></a> <span class="n">DEMO</span> <span class="o">=</span> <span class="s2">"demo"</span>
|
||||
</span><span id="__span-3-6"><a id="__codelineno-3-6" name="__codelineno-3-6" href="#__codelineno-3-6"></a>
|
||||
</span><span id="__span-3-7"><a id="__codelineno-3-7" name="__codelineno-3-7" href="#__codelineno-3-7"></a><span class="nd">@fixtures</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">contexts</span><span class="o">=</span><span class="p">[</span><span class="n">AppContext</span><span class="o">.</span><span class="n">STAGING</span><span class="p">])</span>
|
||||
</span><span id="__span-3-8"><a id="__codelineno-3-8" name="__codelineno-3-8" href="#__codelineno-3-8"></a><span class="k">def</span><span class="w"> </span><span class="nf">staging_data</span><span class="p">():</span>
|
||||
</span><span id="__span-3-9"><a id="__codelineno-3-9" name="__codelineno-3-9" href="#__codelineno-3-9"></a> <span class="k">return</span> <span class="p">[</span><span class="n">Config</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="s2">"feature_x"</span><span class="p">,</span> <span class="n">enabled</span><span class="o">=</span><span class="kc">True</span><span class="p">)]</span>
|
||||
</span><span id="__span-3-10"><a id="__codelineno-3-10" name="__codelineno-3-10" href="#__codelineno-3-10"></a>
|
||||
</span><span id="__span-3-11"><a id="__codelineno-3-11" name="__codelineno-3-11" href="#__codelineno-3-11"></a><span class="k">await</span> <span class="n">load_fixtures_by_context</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">fixtures</span><span class="p">,</span> <span class="n">AppContext</span><span class="o">.</span><span class="n">STAGING</span><span class="p">)</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">enum</span><span class="w"> </span><span class="kn">import</span> <span class="n">Enum</span>
|
||||
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a>
|
||||
</span><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a><span class="k">class</span><span class="w"> </span><span class="nc">AppContext</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="n">Enum</span><span class="p">):</span>
|
||||
</span><span id="__span-4-4"><a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a> <span class="n">STAGING</span> <span class="o">=</span> <span class="s2">"staging"</span>
|
||||
</span><span id="__span-4-5"><a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a> <span class="n">DEMO</span> <span class="o">=</span> <span class="s2">"demo"</span>
|
||||
</span><span id="__span-4-6"><a id="__codelineno-4-6" name="__codelineno-4-6" href="#__codelineno-4-6"></a>
|
||||
</span><span id="__span-4-7"><a id="__codelineno-4-7" name="__codelineno-4-7" href="#__codelineno-4-7"></a><span class="nd">@fixtures</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">contexts</span><span class="o">=</span><span class="p">[</span><span class="n">AppContext</span><span class="o">.</span><span class="n">STAGING</span><span class="p">])</span>
|
||||
</span><span id="__span-4-8"><a id="__codelineno-4-8" name="__codelineno-4-8" href="#__codelineno-4-8"></a><span class="k">def</span><span class="w"> </span><span class="nf">staging_data</span><span class="p">():</span>
|
||||
</span><span id="__span-4-9"><a id="__codelineno-4-9" name="__codelineno-4-9" href="#__codelineno-4-9"></a> <span class="k">return</span> <span class="p">[</span><span class="n">Config</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="s2">"feature_x"</span><span class="p">,</span> <span class="n">enabled</span><span class="o">=</span><span class="kc">True</span><span class="p">)]</span>
|
||||
</span><span id="__span-4-10"><a id="__codelineno-4-10" name="__codelineno-4-10" href="#__codelineno-4-10"></a>
|
||||
</span><span id="__span-4-11"><a id="__codelineno-4-11" name="__codelineno-4-11" href="#__codelineno-4-11"></a><span class="c1"># loads staging_data plus any Context.BASE fixtures</span>
|
||||
</span><span id="__span-4-12"><a id="__codelineno-4-12" name="__codelineno-4-12" href="#__codelineno-4-12"></a><span class="k">await</span> <span class="n">load_fixtures_by_context</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">fixtures</span><span class="p">,</span> <span class="n">AppContext</span><span class="o">.</span><span class="n">STAGING</span><span class="p">)</span>
|
||||
</span></code></pre></div>
|
||||
<h3 id="default-context-for-a-registry">Default context for a registry<a class="headerlink" href="#default-context-for-a-registry" title="Permanent link">¶</a></h3>
|
||||
<p>Pass <code>contexts</code> to <code>FixtureRegistry</code> to set a default for all fixtures registered in it:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="n">testing_registry</span> <span class="o">=</span> <span class="n">FixtureRegistry</span><span class="p">(</span><span class="n">contexts</span><span class="o">=</span><span class="p">[</span><span class="n">Context</span><span class="o">.</span><span class="n">TESTING</span><span class="p">])</span>
|
||||
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a>
|
||||
</span><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a><span class="nd">@testing_registry</span><span class="o">.</span><span class="n">register</span> <span class="c1"># implicitly contexts=[Context.TESTING]</span>
|
||||
</span><span id="__span-4-4"><a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a><span class="k">def</span><span class="w"> </span><span class="nf">test_orders</span><span class="p">():</span>
|
||||
</span><span id="__span-4-5"><a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a> <span class="k">return</span> <span class="p">[</span><span class="n">Order</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">total</span><span class="o">=</span><span class="mi">99</span><span class="p">)]</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="n">testing_registry</span> <span class="o">=</span> <span class="n">FixtureRegistry</span><span class="p">(</span><span class="n">contexts</span><span class="o">=</span><span class="p">[</span><span class="n">Context</span><span class="o">.</span><span class="n">TESTING</span><span class="p">])</span>
|
||||
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a>
|
||||
</span><span id="__span-5-3"><a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a><span class="nd">@testing_registry</span><span class="o">.</span><span class="n">register</span> <span class="c1"># implicitly contexts=[Context.TESTING]</span>
|
||||
</span><span id="__span-5-4"><a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a><span class="k">def</span><span class="w"> </span><span class="nf">test_orders</span><span class="p">():</span>
|
||||
</span><span id="__span-5-5"><a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a> <span class="k">return</span> <span class="p">[</span><span class="n">Order</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">total</span><span class="o">=</span><span class="mi">99</span><span class="p">)]</span>
|
||||
</span></code></pre></div>
|
||||
<h3 id="same-fixture-name-multiple-context-variants">Same fixture name, multiple context variants<a class="headerlink" href="#same-fixture-name-multiple-context-variants" title="Permanent link">¶</a></h3>
|
||||
<p>The same fixture name may be registered under different (non-overlapping) context sets. When multiple contexts are loaded together, all matching variants are merged:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="nd">@fixtures</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">contexts</span><span class="o">=</span><span class="p">[</span><span class="n">Context</span><span class="o">.</span><span class="n">BASE</span><span class="p">])</span>
|
||||
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a><span class="k">def</span><span class="w"> </span><span class="nf">users</span><span class="p">():</span>
|
||||
</span><span id="__span-5-3"><a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a> <span class="k">return</span> <span class="p">[</span><span class="n">User</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="s2">"admin"</span><span class="p">)]</span>
|
||||
</span><span id="__span-5-4"><a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a>
|
||||
</span><span id="__span-5-5"><a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a><span class="nd">@fixtures</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">contexts</span><span class="o">=</span><span class="p">[</span><span class="n">Context</span><span class="o">.</span><span class="n">TESTING</span><span class="p">])</span>
|
||||
</span><span id="__span-5-6"><a id="__codelineno-5-6" name="__codelineno-5-6" href="#__codelineno-5-6"></a><span class="k">def</span><span class="w"> </span><span class="nf">users</span><span class="p">():</span>
|
||||
</span><span id="__span-5-7"><a id="__codelineno-5-7" name="__codelineno-5-7" href="#__codelineno-5-7"></a> <span class="k">return</span> <span class="p">[</span><span class="n">User</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="s2">"tester"</span><span class="p">)]</span>
|
||||
</span><span id="__span-5-8"><a id="__codelineno-5-8" name="__codelineno-5-8" href="#__codelineno-5-8"></a>
|
||||
</span><span id="__span-5-9"><a id="__codelineno-5-9" name="__codelineno-5-9" href="#__codelineno-5-9"></a><span class="c1"># loads both admin and tester</span>
|
||||
</span><span id="__span-5-10"><a id="__codelineno-5-10" name="__codelineno-5-10" href="#__codelineno-5-10"></a><span class="k">await</span> <span class="n">load_fixtures_by_context</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">fixtures</span><span class="p">,</span> <span class="n">Context</span><span class="o">.</span><span class="n">BASE</span><span class="p">,</span> <span class="n">Context</span><span class="o">.</span><span class="n">TESTING</span><span class="p">)</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-6-1"><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="nd">@fixtures</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">contexts</span><span class="o">=</span><span class="p">[</span><span class="n">Context</span><span class="o">.</span><span class="n">BASE</span><span class="p">])</span>
|
||||
</span><span id="__span-6-2"><a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a><span class="k">def</span><span class="w"> </span><span class="nf">users</span><span class="p">():</span>
|
||||
</span><span id="__span-6-3"><a id="__codelineno-6-3" name="__codelineno-6-3" href="#__codelineno-6-3"></a> <span class="k">return</span> <span class="p">[</span><span class="n">User</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="s2">"admin"</span><span class="p">)]</span>
|
||||
</span><span id="__span-6-4"><a id="__codelineno-6-4" name="__codelineno-6-4" href="#__codelineno-6-4"></a>
|
||||
</span><span id="__span-6-5"><a id="__codelineno-6-5" name="__codelineno-6-5" href="#__codelineno-6-5"></a><span class="nd">@fixtures</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">contexts</span><span class="o">=</span><span class="p">[</span><span class="n">Context</span><span class="o">.</span><span class="n">TESTING</span><span class="p">])</span>
|
||||
</span><span id="__span-6-6"><a id="__codelineno-6-6" name="__codelineno-6-6" href="#__codelineno-6-6"></a><span class="k">def</span><span class="w"> </span><span class="nf">users</span><span class="p">():</span>
|
||||
</span><span id="__span-6-7"><a id="__codelineno-6-7" name="__codelineno-6-7" href="#__codelineno-6-7"></a> <span class="k">return</span> <span class="p">[</span><span class="n">User</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="s2">"tester"</span><span class="p">)]</span>
|
||||
</span><span id="__span-6-8"><a id="__codelineno-6-8" name="__codelineno-6-8" href="#__codelineno-6-8"></a>
|
||||
</span><span id="__span-6-9"><a id="__codelineno-6-9" name="__codelineno-6-9" href="#__codelineno-6-9"></a><span class="c1"># loads both admin and tester (Context.BASE is included automatically)</span>
|
||||
</span><span id="__span-6-10"><a id="__codelineno-6-10" name="__codelineno-6-10" href="#__codelineno-6-10"></a><span class="k">await</span> <span class="n">load_fixtures_by_context</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">fixtures</span><span class="p">,</span> <span class="n">Context</span><span class="o">.</span><span class="n">TESTING</span><span class="p">)</span>
|
||||
</span></code></pre></div>
|
||||
<p>Registering two variants with overlapping context sets raises <code>ValueError</code>.</p>
|
||||
<h2 id="load-strategies">Load strategies<a class="headerlink" href="#load-strategies" title="Permanent link">¶</a></h2>
|
||||
@@ -2138,50 +2143,54 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-6-1"><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="k">await</span> <span class="n">load_fixtures_by_context</span><span class="p">(</span>
|
||||
</span><span id="__span-6-2"><a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a> <span class="n">session</span><span class="p">,</span> <span class="n">fixtures</span><span class="p">,</span> <span class="n">Context</span><span class="o">.</span><span class="n">BASE</span><span class="p">,</span> <span class="n">strategy</span><span class="o">=</span><span class="n">LoadStrategy</span><span class="o">.</span><span class="n">SKIP_EXISTING</span>
|
||||
</span><span id="__span-6-3"><a id="__codelineno-6-3" name="__codelineno-6-3" href="#__codelineno-6-3"></a><span class="p">)</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a><span class="k">await</span> <span class="n">load_fixtures_by_context</span><span class="p">(</span>
|
||||
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a> <span class="n">session</span><span class="p">,</span> <span class="n">fixtures</span><span class="p">,</span> <span class="n">Context</span><span class="o">.</span><span class="n">BASE</span><span class="p">,</span> <span class="n">strategy</span><span class="o">=</span><span class="n">LoadStrategy</span><span class="o">.</span><span class="n">SKIP_EXISTING</span>
|
||||
</span><span id="__span-7-3"><a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a><span class="p">)</span>
|
||||
</span></code></pre></div>
|
||||
<h2 id="merging-registries">Merging registries<a class="headerlink" href="#merging-registries" title="Permanent link">¶</a></h2>
|
||||
<p>Split fixture definitions across modules and merge them:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">myapp.fixtures.dev</span><span class="w"> </span><span class="kn">import</span> <span class="n">dev_fixtures</span>
|
||||
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">myapp.fixtures.prod</span><span class="w"> </span><span class="kn">import</span> <span class="n">prod_fixtures</span>
|
||||
</span><span id="__span-7-3"><a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a>
|
||||
</span><span id="__span-7-4"><a id="__codelineno-7-4" name="__codelineno-7-4" href="#__codelineno-7-4"></a><span class="n">fixtures</span> <span class="o">=</span> <span class="n">FixtureRegistry</span><span class="p">()</span>
|
||||
</span><span id="__span-7-5"><a id="__codelineno-7-5" name="__codelineno-7-5" href="#__codelineno-7-5"></a><span class="n">fixtures</span><span class="o">.</span><span class="n">include_registry</span><span class="p">(</span><span class="n">registry</span><span class="o">=</span><span class="n">dev_fixtures</span><span class="p">)</span>
|
||||
</span><span id="__span-7-6"><a id="__codelineno-7-6" name="__codelineno-7-6" href="#__codelineno-7-6"></a><span class="n">fixtures</span><span class="o">.</span><span class="n">include_registry</span><span class="p">(</span><span class="n">registry</span><span class="o">=</span><span class="n">prod_fixtures</span><span class="p">)</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-8-1"><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">myapp.fixtures.dev</span><span class="w"> </span><span class="kn">import</span> <span class="n">dev_fixtures</span>
|
||||
</span><span id="__span-8-2"><a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">myapp.fixtures.prod</span><span class="w"> </span><span class="kn">import</span> <span class="n">prod_fixtures</span>
|
||||
</span><span id="__span-8-3"><a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a>
|
||||
</span><span id="__span-8-4"><a id="__codelineno-8-4" name="__codelineno-8-4" href="#__codelineno-8-4"></a><span class="n">fixtures</span> <span class="o">=</span> <span class="n">FixtureRegistry</span><span class="p">()</span>
|
||||
</span><span id="__span-8-5"><a id="__codelineno-8-5" name="__codelineno-8-5" href="#__codelineno-8-5"></a><span class="n">fixtures</span><span class="o">.</span><span class="n">include_registry</span><span class="p">(</span><span class="n">registry</span><span class="o">=</span><span class="n">dev_fixtures</span><span class="p">)</span>
|
||||
</span><span id="__span-8-6"><a id="__codelineno-8-6" name="__codelineno-8-6" href="#__codelineno-8-6"></a><span class="n">fixtures</span><span class="o">.</span><span class="n">include_registry</span><span class="p">(</span><span class="n">registry</span><span class="o">=</span><span class="n">prod_fixtures</span><span class="p">)</span>
|
||||
</span></code></pre></div>
|
||||
<p>Fixtures with the same name are allowed as long as their context sets do not overlap. Conflicting contexts raise <code>ValueError</code>.</p>
|
||||
<h2 id="looking-up-fixture-instances">Looking up fixture instances<a class="headerlink" href="#looking-up-fixture-instances" title="Permanent link">¶</a></h2>
|
||||
<p><a href="../../reference/fixtures/#fastapi_toolsets.fixtures.utils.get_obj_by_attr"><code>get_obj_by_attr</code></a> retrieves a specific instance from a fixture function by attribute value — useful when building cross-fixture <code>depends_on</code> relationships:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-8-1"><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.fixtures</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_obj_by_attr</span>
|
||||
</span><span id="__span-8-2"><a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a>
|
||||
</span><span id="__span-8-3"><a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a><span class="nd">@fixtures</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">depends_on</span><span class="o">=</span><span class="p">[</span><span class="s2">"roles"</span><span class="p">])</span>
|
||||
</span><span id="__span-8-4"><a id="__codelineno-8-4" name="__codelineno-8-4" href="#__codelineno-8-4"></a><span class="k">def</span><span class="w"> </span><span class="nf">users</span><span class="p">():</span>
|
||||
</span><span id="__span-8-5"><a id="__codelineno-8-5" name="__codelineno-8-5" href="#__codelineno-8-5"></a> <span class="n">admin_role</span> <span class="o">=</span> <span class="n">get_obj_by_attr</span><span class="p">(</span><span class="n">roles</span><span class="p">,</span> <span class="s2">"name"</span><span class="p">,</span> <span class="s2">"admin"</span><span class="p">)</span>
|
||||
</span><span id="__span-8-6"><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="n">User</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="s2">"alice"</span><span class="p">,</span> <span class="n">role_id</span><span class="o">=</span><span class="n">admin_role</span><span class="o">.</span><span class="n">id</span><span class="p">)]</span>
|
||||
<p><a href="../../reference/fixtures/#fastapi_toolsets.fixtures.registry.FixtureRegistry.obj"><code>FixtureRegistry.obj</code></a> retrieves a specific instance from a registered fixture by attribute value, looked up by name on the registry — useful when building cross-fixture <code>depends_on</code> relationships:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-9-1"><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="nd">@fixtures</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">depends_on</span><span class="o">=</span><span class="p">[</span><span class="s2">"roles"</span><span class="p">])</span>
|
||||
</span><span id="__span-9-2"><a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a><span class="k">def</span><span class="w"> </span><span class="nf">users</span><span class="p">():</span>
|
||||
</span><span id="__span-9-3"><a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a> <span class="n">admin_role</span> <span class="o">=</span> <span class="n">fixtures</span><span class="o">.</span><span class="n">obj</span><span class="p">(</span><span class="s2">"roles"</span><span class="p">,</span> <span class="s2">"name"</span><span class="p">,</span> <span class="s2">"admin"</span><span class="p">)</span>
|
||||
</span><span id="__span-9-4"><a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a> <span class="k">return</span> <span class="p">[</span><span class="n">User</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="s2">"alice"</span><span class="p">,</span> <span class="n">role_id</span><span class="o">=</span><span class="n">admin_role</span><span class="o">.</span><span class="n">id</span><span class="p">)]</span>
|
||||
</span></code></pre></div>
|
||||
<p>Raises <code>StopIteration</code> if no matching instance is found.</p>
|
||||
<p>Looking the fixture up by name (instead of importing the <code>roles</code> function directly) means fixture modules never need to import each other, which avoids circular imports in larger projects split across multiple files — the same reason <code>depends_on</code> takes fixture names rather than the functions themselves. The registry passed in must be the one that actually contains the fixture by load time; with a single shared registry this is automatic, but if you merge registries with <code>include_registry</code>, call <code>obj</code>/<code>field</code> on the merged registry.</p>
|
||||
<p><a href="../../reference/fixtures/#fastapi_toolsets.fixtures.registry.FixtureRegistry.field"><code>FixtureRegistry.field</code></a> is shorthand for pulling a single attribute (<code>id</code> by default):</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-10-1"><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a><span class="nd">@fixtures</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">depends_on</span><span class="o">=</span><span class="p">[</span><span class="s2">"roles"</span><span class="p">])</span>
|
||||
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a><span class="k">def</span><span class="w"> </span><span class="nf">users</span><span class="p">():</span>
|
||||
</span><span id="__span-10-3"><a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a> <span class="k">return</span> <span class="p">[</span><span class="n">User</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="s2">"alice"</span><span class="p">,</span> <span class="n">role_id</span><span class="o">=</span><span class="n">fixtures</span><span class="o">.</span><span class="n">field</span><span class="p">(</span><span class="s2">"roles"</span><span class="p">,</span> <span class="s2">"name"</span><span class="p">,</span> <span class="s2">"admin"</span><span class="p">))]</span>
|
||||
</span></code></pre></div>
|
||||
<p>Both raise <code>StopIteration</code> if no matching instance is found, and <code>KeyError</code> if the fixture name isn't registered.</p>
|
||||
<h2 id="pytest-integration">Pytest integration<a class="headerlink" href="#pytest-integration" title="Permanent link">¶</a></h2>
|
||||
<p>Use <a href="../../reference/pytest/#fastapi_toolsets.pytest.plugin.register_fixtures"><code>register_fixtures</code></a> to expose each fixture in your registry as an injectable pytest fixture named <code>fixture_{name}</code> by default:</p>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-9-1"><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="c1"># conftest.py</span>
|
||||
</span><span id="__span-9-2"><a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a><span class="kn">import</span><span class="w"> </span><span class="nn">pytest</span>
|
||||
</span><span id="__span-9-3"><a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.pytest</span><span class="w"> </span><span class="kn">import</span> <span class="n">create_db_session</span><span class="p">,</span> <span class="n">register_fixtures</span>
|
||||
</span><span id="__span-9-4"><a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">app.fixtures</span><span class="w"> </span><span class="kn">import</span> <span class="n">registry</span>
|
||||
</span><span id="__span-9-5"><a id="__codelineno-9-5" name="__codelineno-9-5" href="#__codelineno-9-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">app.models</span><span class="w"> </span><span class="kn">import</span> <span class="n">Base</span>
|
||||
</span><span id="__span-9-6"><a id="__codelineno-9-6" name="__codelineno-9-6" href="#__codelineno-9-6"></a>
|
||||
</span><span id="__span-9-7"><a id="__codelineno-9-7" name="__codelineno-9-7" href="#__codelineno-9-7"></a><span class="n">DATABASE_URL</span> <span class="o">=</span> <span class="s2">"postgresql+asyncpg://user:pass@localhost/test_db"</span>
|
||||
</span><span id="__span-9-8"><a id="__codelineno-9-8" name="__codelineno-9-8" href="#__codelineno-9-8"></a>
|
||||
</span><span id="__span-9-9"><a id="__codelineno-9-9" name="__codelineno-9-9" href="#__codelineno-9-9"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
|
||||
</span><span id="__span-9-10"><a id="__codelineno-9-10" name="__codelineno-9-10" href="#__codelineno-9-10"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">db_session</span><span class="p">():</span>
|
||||
</span><span id="__span-9-11"><a id="__codelineno-9-11" name="__codelineno-9-11" href="#__codelineno-9-11"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">create_db_session</span><span class="p">(</span><span class="n">database_url</span><span class="o">=</span><span class="n">DATABASE_URL</span><span class="p">,</span> <span class="n">base</span><span class="o">=</span><span class="n">Base</span><span class="p">,</span> <span class="n">cleanup</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||||
</span><span id="__span-9-12"><a id="__codelineno-9-12" name="__codelineno-9-12" href="#__codelineno-9-12"></a> <span class="k">yield</span> <span class="n">session</span>
|
||||
</span><span id="__span-9-13"><a id="__codelineno-9-13" name="__codelineno-9-13" href="#__codelineno-9-13"></a>
|
||||
</span><span id="__span-9-14"><a id="__codelineno-9-14" name="__codelineno-9-14" href="#__codelineno-9-14"></a><span class="n">register_fixtures</span><span class="p">(</span><span class="n">registry</span><span class="o">=</span><span class="n">registry</span><span class="p">,</span> <span class="n">namespace</span><span class="o">=</span><span class="nb">globals</span><span class="p">())</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-11-1"><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="c1"># conftest.py</span>
|
||||
</span><span id="__span-11-2"><a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a><span class="kn">import</span><span class="w"> </span><span class="nn">pytest</span>
|
||||
</span><span id="__span-11-3"><a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.pytest</span><span class="w"> </span><span class="kn">import</span> <span class="n">create_db_session</span><span class="p">,</span> <span class="n">register_fixtures</span>
|
||||
</span><span id="__span-11-4"><a id="__codelineno-11-4" name="__codelineno-11-4" href="#__codelineno-11-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">app.fixtures</span><span class="w"> </span><span class="kn">import</span> <span class="n">registry</span>
|
||||
</span><span id="__span-11-5"><a id="__codelineno-11-5" name="__codelineno-11-5" href="#__codelineno-11-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">app.models</span><span class="w"> </span><span class="kn">import</span> <span class="n">Base</span>
|
||||
</span><span id="__span-11-6"><a id="__codelineno-11-6" name="__codelineno-11-6" href="#__codelineno-11-6"></a>
|
||||
</span><span id="__span-11-7"><a id="__codelineno-11-7" name="__codelineno-11-7" href="#__codelineno-11-7"></a><span class="n">DATABASE_URL</span> <span class="o">=</span> <span class="s2">"postgresql+asyncpg://user:pass@localhost/test_db"</span>
|
||||
</span><span id="__span-11-8"><a id="__codelineno-11-8" name="__codelineno-11-8" href="#__codelineno-11-8"></a>
|
||||
</span><span id="__span-11-9"><a id="__codelineno-11-9" name="__codelineno-11-9" href="#__codelineno-11-9"></a><span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
|
||||
</span><span id="__span-11-10"><a id="__codelineno-11-10" name="__codelineno-11-10" href="#__codelineno-11-10"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">db_session</span><span class="p">():</span>
|
||||
</span><span id="__span-11-11"><a id="__codelineno-11-11" name="__codelineno-11-11" href="#__codelineno-11-11"></a> <span class="k">async</span> <span class="k">with</span> <span class="n">create_db_session</span><span class="p">(</span><span class="n">database_url</span><span class="o">=</span><span class="n">DATABASE_URL</span><span class="p">,</span> <span class="n">base</span><span class="o">=</span><span class="n">Base</span><span class="p">,</span> <span class="n">cleanup</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||||
</span><span id="__span-11-12"><a id="__codelineno-11-12" name="__codelineno-11-12" href="#__codelineno-11-12"></a> <span class="k">yield</span> <span class="n">session</span>
|
||||
</span><span id="__span-11-13"><a id="__codelineno-11-13" name="__codelineno-11-13" href="#__codelineno-11-13"></a>
|
||||
</span><span id="__span-11-14"><a id="__codelineno-11-14" name="__codelineno-11-14" href="#__codelineno-11-14"></a><span class="n">register_fixtures</span><span class="p">(</span><span class="n">registry</span><span class="o">=</span><span class="n">registry</span><span class="p">,</span> <span class="n">namespace</span><span class="o">=</span><span class="nb">globals</span><span class="p">())</span>
|
||||
</span></code></pre></div>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-10-1"><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a><span class="c1"># test_users.py</span>
|
||||
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">test_user_can_login</span><span class="p">(</span><span class="n">fixture_users</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">User</span><span class="p">],</span> <span class="n">fixture_roles</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">Role</span><span class="p">]):</span>
|
||||
</span><span id="__span-10-3"><a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a> <span class="o">...</span>
|
||||
<div class="language-python highlight"><pre><span></span><code><span id="__span-12-1"><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a><span class="c1"># test_users.py</span>
|
||||
</span><span id="__span-12-2"><a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">test_user_can_login</span><span class="p">(</span><span class="n">fixture_users</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">User</span><span class="p">],</span> <span class="n">fixture_roles</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">Role</span><span class="p">]):</span>
|
||||
</span><span id="__span-12-3"><a id="__codelineno-12-3" name="__codelineno-12-3" href="#__codelineno-12-3"></a> <span class="o">...</span>
|
||||
</span></code></pre></div>
|
||||
<p>The load order is resolved automatically from the <code>depends_on</code> declarations in your registry. Each generated fixture receives <code>db_session</code> as a dependency and returns the list of loaded model instances.</p>
|
||||
<h2 id="cli-integration">CLI integration<a class="headerlink" href="#cli-integration" title="Permanent link">¶</a></h2>
|
||||
|
||||
Binary file not shown.
@@ -2176,11 +2176,12 @@ untouched).</p>
|
||||
<code>url</code>
|
||||
</td>
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/stdtypes.html#str">str</a> | None</code>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/stdtypes.html#str">str</a> | <span title="pydantic.PostgresDsn">PostgresDsn</span> | None</code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>Database connection URL (e.g. <code>"postgresql+asyncpg://..."</code>).</p>
|
||||
<p>Database connection URL. Accepts a plain string or a Pydantic
|
||||
:class:<code>~pydantic.PostgresDsn</code>.</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -2251,6 +2252,23 @@ untouched).</p>
|
||||
<code>True</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code>connect_args</code>
|
||||
</td>
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/stdtypes.html#dict">dict</a>[<a class="autorefs autorefs-external" href="https://docs.python.org/3/library/stdtypes.html#str">str</a>, <a class="autorefs autorefs-external" title="typing.Any" href="https://docs.python.org/3/library/typing.html#typing.Any">Any</a>] | None</code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>DBAPI-level connection arguments forwarded to
|
||||
:func:<code>create_async_engine</code> (URL mode only).</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<code>None</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code>**engine_options</code>
|
||||
@@ -2261,8 +2279,7 @@ untouched).</p>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>Extra keyword arguments forwarded to
|
||||
:func:<code>create_async_engine</code> (URL mode only, e.g. <code>pool_size</code>,
|
||||
<code>echo</code>, <code>connect_args</code>).</p>
|
||||
:func:<code>create_async_engine</code> (URL mode only).</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -2289,7 +2306,7 @@ untouched).</p>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>If neither or both of <em>url</em> and <em>engine</em> are given, or if
|
||||
<em>engine_options</em> are passed together with <em>engine</em>.</p>
|
||||
<em>connect_args</em>/<em>engine_options</em> are passed together with <em>engine</em>.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
+466
-158
@@ -1286,6 +1286,17 @@
|
||||
<nav class="md-nav" aria-label="FixtureRegistry">
|
||||
<ul class="md-nav__list">
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#fastapi_toolsets.fixtures.registry.FixtureRegistry.field" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
<span class="md-typeset">
|
||||
field
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#fastapi_toolsets.fixtures.registry.FixtureRegistry.get" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
@@ -1317,6 +1328,28 @@
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#fastapi_toolsets.fixtures.registry.FixtureRegistry.get_dependencies" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
<span class="md-typeset">
|
||||
get_dependencies
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#fastapi_toolsets.fixtures.registry.FixtureRegistry.get_load_variants" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
<span class="md-typeset">
|
||||
get_load_variants
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
@@ -1339,6 +1372,17 @@
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#fastapi_toolsets.fixtures.registry.FixtureRegistry.obj" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
<span class="md-typeset">
|
||||
obj
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
@@ -1399,17 +1443,6 @@
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#fastapi_toolsets.fixtures.utils.get_obj_by_attr" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
<span class="md-typeset">
|
||||
get_obj_by_attr
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
@@ -2024,6 +2057,17 @@
|
||||
<nav class="md-nav" aria-label="FixtureRegistry">
|
||||
<ul class="md-nav__list">
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#fastapi_toolsets.fixtures.registry.FixtureRegistry.field" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
<span class="md-typeset">
|
||||
field
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#fastapi_toolsets.fixtures.registry.FixtureRegistry.get" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
@@ -2055,6 +2099,28 @@
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#fastapi_toolsets.fixtures.registry.FixtureRegistry.get_dependencies" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
<span class="md-typeset">
|
||||
get_dependencies
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#fastapi_toolsets.fixtures.registry.FixtureRegistry.get_load_variants" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
<span class="md-typeset">
|
||||
get_load_variants
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
@@ -2077,6 +2143,17 @@
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#fastapi_toolsets.fixtures.registry.FixtureRegistry.obj" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
<span class="md-typeset">
|
||||
obj
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
@@ -2137,17 +2214,6 @@
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#fastapi_toolsets.fixtures.utils.get_obj_by_attr" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
<span class="md-typeset">
|
||||
get_obj_by_attr
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
@@ -2265,8 +2331,7 @@
|
||||
</span><span id="__span-0-5"><a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a> <span class="n">FixtureRegistry</span><span class="p">,</span>
|
||||
</span><span id="__span-0-6"><a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a> <span class="n">load_fixtures</span><span class="p">,</span>
|
||||
</span><span id="__span-0-7"><a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a> <span class="n">load_fixtures_by_context</span><span class="p">,</span>
|
||||
</span><span id="__span-0-8"><a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="n">get_obj_by_attr</span><span class="p">,</span>
|
||||
</span><span id="__span-0-9"><a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="p">)</span>
|
||||
</span><span id="__span-0-8"><a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="p">)</span>
|
||||
</span></code></pre></div>
|
||||
|
||||
|
||||
@@ -2630,8 +2695,6 @@ def users():
|
||||
@fixtures.register(contexts=[Context.TESTING])
|
||||
def users():
|
||||
return [User(id=2, username="tester")]
|
||||
# load_fixtures_by_context(..., Context.BASE, Context.TESTING)
|
||||
# → loads both User(admin) and User(tester) under the "users" name
|
||||
```
|
||||
</code></pre></div>
|
||||
|
||||
@@ -2656,6 +2719,159 @@ def users():
|
||||
|
||||
|
||||
|
||||
<div class="doc doc-object doc-function">
|
||||
|
||||
|
||||
<h3 id="fastapi_toolsets.fixtures.registry.FixtureRegistry.field" class="doc doc-heading">
|
||||
<code class="highlight language-python"><span class="n">field</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">attr_name</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">field</span><span class="o">=</span><span class="s1">'id'</span><span class="p">)</span></code>
|
||||
|
||||
<a href="#fastapi_toolsets.fixtures.registry.FixtureRegistry.field" class="headerlink" title="Permanent link">¶</a></h3>
|
||||
|
||||
|
||||
<div class="doc doc-contents ">
|
||||
|
||||
<p>Get a single field value from a fixture object matched by an attribute.</p>
|
||||
|
||||
|
||||
<p><span class="doc-section-title">Parameters:</span></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code>name</code>
|
||||
</td>
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/stdtypes.html#str">str</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>Fixture name to look up.</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<em>required</em>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code>attr_name</code>
|
||||
</td>
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/stdtypes.html#str">str</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>Name of the attribute to match against.</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<em>required</em>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code>value</code>
|
||||
</td>
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" title="typing.Any" href="https://docs.python.org/3/library/typing.html#typing.Any">Any</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>Value to match.</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<em>required</em>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code>field</code>
|
||||
</td>
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/stdtypes.html#str">str</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>Attribute name to return from the matched object (default: <code>"id"</code>).</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<code>'id'</code>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<p><span class="doc-section-title">Returns:</span></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" title="typing.Any" href="https://docs.python.org/3/library/typing.html#typing.Any">Any</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>The value of <code>field</code> on the first matching model instance.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<p><span class="doc-section-title">Raises:</span></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/exceptions.html#KeyError">KeyError</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>If no fixture named <em>name</em> is registered.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/exceptions.html#StopIteration">StopIteration</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>If no matching object is found.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="doc doc-object doc-function">
|
||||
|
||||
|
||||
@@ -2747,6 +2963,88 @@ def users():
|
||||
<div class="doc doc-object doc-function">
|
||||
|
||||
|
||||
<h3 id="fastapi_toolsets.fixtures.registry.FixtureRegistry.get_dependencies" class="doc doc-heading">
|
||||
<code class="highlight language-python"><span class="n">get_dependencies</span><span class="p">(</span><span class="n">name</span><span class="p">)</span></code>
|
||||
|
||||
<a href="#fastapi_toolsets.fixtures.registry.FixtureRegistry.get_dependencies" class="headerlink" title="Permanent link">¶</a></h3>
|
||||
|
||||
|
||||
<div class="doc doc-contents ">
|
||||
|
||||
<p>Get the union of <code>depends_on</code> across all variants of <em>name</em>.</p>
|
||||
|
||||
|
||||
<p><span class="doc-section-title">Raises:</span></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/exceptions.html#KeyError">KeyError</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>If no fixture named <em>name</em> is registered.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="doc doc-object doc-function">
|
||||
|
||||
|
||||
<h3 id="fastapi_toolsets.fixtures.registry.FixtureRegistry.get_load_variants" class="doc doc-heading">
|
||||
<code class="highlight language-python"><span class="n">get_load_variants</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="o">*</span><span class="n">contexts</span><span class="p">)</span></code>
|
||||
|
||||
<a href="#fastapi_toolsets.fixtures.registry.FixtureRegistry.get_load_variants" class="headerlink" title="Permanent link">¶</a></h3>
|
||||
|
||||
|
||||
<div class="doc doc-contents ">
|
||||
|
||||
<p>Return variants for <em>name</em> filtered by <em>contexts</em>.</p>
|
||||
|
||||
|
||||
<p><span class="doc-section-title">Raises:</span></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/exceptions.html#KeyError">KeyError</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>If no fixture with <em>name</em> is registered.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="doc doc-object doc-function">
|
||||
|
||||
|
||||
<h3 id="fastapi_toolsets.fixtures.registry.FixtureRegistry.get_variants" class="doc doc-heading">
|
||||
<code class="highlight language-python"><span class="n">get_variants</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="o">*</span><span class="n">contexts</span><span class="p">)</span></code>
|
||||
|
||||
@@ -2795,8 +3093,9 @@ def users():
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>If given, only return variants whose context set
|
||||
intersects with these values. Both :class:<code>Context</code> enum
|
||||
values and plain strings are accepted.</p>
|
||||
intersects with these values (:class:<code>Context.BASE</code> variants
|
||||
are always included). Both :class:<code>Context</code> enum values and
|
||||
plain strings are accepted.</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -2967,6 +3266,143 @@ do not overlap. Conflicting contexts raise :class:<code>ValueError</code>.</p>
|
||||
<div class="doc doc-object doc-function">
|
||||
|
||||
|
||||
<h3 id="fastapi_toolsets.fixtures.registry.FixtureRegistry.obj" class="doc doc-heading">
|
||||
<code class="highlight language-python"><span class="n">obj</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">attr_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span></code>
|
||||
|
||||
<a href="#fastapi_toolsets.fixtures.registry.FixtureRegistry.obj" class="headerlink" title="Permanent link">¶</a></h3>
|
||||
|
||||
|
||||
<div class="doc doc-contents ">
|
||||
|
||||
<p>Get a model instance from a registered fixture by attribute value.</p>
|
||||
|
||||
|
||||
<p><span class="doc-section-title">Parameters:</span></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code>name</code>
|
||||
</td>
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/stdtypes.html#str">str</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>Fixture name to look up.</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<em>required</em>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code>attr_name</code>
|
||||
</td>
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/stdtypes.html#str">str</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>Name of the attribute to match against.</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<em>required</em>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code>value</code>
|
||||
</td>
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" title="typing.Any" href="https://docs.python.org/3/library/typing.html#typing.Any">Any</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>Value to match.</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<em>required</em>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<p><span class="doc-section-title">Returns:</span></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code><span title="sqlalchemy.orm.DeclarativeBase">DeclarativeBase</span></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>The first model instance where the attribute matches the given value.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<p><span class="doc-section-title">Raises:</span></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/exceptions.html#KeyError">KeyError</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>If no fixture named <em>name</em> is registered.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/exceptions.html#StopIteration">StopIteration</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>If no matching object is found.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="doc doc-object doc-function">
|
||||
|
||||
|
||||
<h3 id="fastapi_toolsets.fixtures.registry.FixtureRegistry.register" class="doc doc-heading">
|
||||
<code class="highlight language-python"><span class="n">register</span><span class="p">(</span><span class="n">func</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">depends_on</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">contexts</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span></code>
|
||||
|
||||
@@ -3461,8 +3897,8 @@ variants' <code>depends_on</code> lists is used.</p>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>Contexts to load (e.g., <code>Context.BASE</code>, <code>Context.TESTING</code>,
|
||||
or plain strings for custom contexts)</p>
|
||||
<p>Contexts to load (e.g., <code>Context.TESTING</code>, or plain
|
||||
strings for custom contexts)</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -3521,134 +3957,6 @@ or plain strings for custom contexts)</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="doc doc-object doc-function">
|
||||
|
||||
|
||||
<h2 id="fastapi_toolsets.fixtures.utils.get_obj_by_attr" class="doc doc-heading">
|
||||
<code class="highlight language-python"><span class="n">fastapi_toolsets</span><span class="o">.</span><span class="n">fixtures</span><span class="o">.</span><span class="n">utils</span><span class="o">.</span><span class="n">get_obj_by_attr</span><span class="p">(</span><span class="n">fixtures</span><span class="p">,</span> <span class="n">attr_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span></code>
|
||||
|
||||
<a href="#fastapi_toolsets.fixtures.utils.get_obj_by_attr" class="headerlink" title="Permanent link">¶</a></h2>
|
||||
|
||||
|
||||
<div class="doc doc-contents first">
|
||||
|
||||
<p>Get a SQLAlchemy model instance by matching an attribute value.</p>
|
||||
|
||||
|
||||
<p><span class="doc-section-title">Parameters:</span></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code>fixtures</code>
|
||||
</td>
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" title="collections.abc.Callable" href="https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable">Callable</a>[[], <a class="autorefs autorefs-external" title="collections.abc.Sequence" href="https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence">Sequence</a>[<span title="fastapi_toolsets.types.ModelType">ModelType</span>]]</code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>A fixture function registered via <code>@registry.register</code>
|
||||
that returns a sequence of SQLAlchemy model instances.</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<em>required</em>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code>attr_name</code>
|
||||
</td>
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/stdtypes.html#str">str</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>Name of the attribute to match against.</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<em>required</em>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code>value</code>
|
||||
</td>
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" title="typing.Any" href="https://docs.python.org/3/library/typing.html#typing.Any">Any</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>Value to match.</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<em>required</em>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<p><span class="doc-section-title">Returns:</span></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code><span title="fastapi_toolsets.types.ModelType">ModelType</span></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>The first model instance where the attribute matches the given value.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<p><span class="doc-section-title">Raises:</span></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="doc-section-item">
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-external" href="https://docs.python.org/3/library/exceptions.html#StopIteration">StopIteration</a></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
<p>If no matching object is found in the fixture group.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1891,7 +1891,7 @@ Dependencies are resolved via pytest fixture dependencies.</p>
|
||||
<code>registry</code>
|
||||
</td>
|
||||
<td>
|
||||
<code><a class="autorefs autorefs-internal" title="<code>fastapi_toolsets.fixtures.registry.FixtureRegistry</code>" href="../fixtures/#fastapi_toolsets.fixtures.registry.FixtureRegistry">FixtureRegistry</a></code>
|
||||
<code><span title="fastapi_toolsets.fixtures.FixtureRegistry">FixtureRegistry</span></code>
|
||||
</td>
|
||||
<td>
|
||||
<div class="doc-md-description">
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user