mirror of
https://github.com/d3vyce/fastapi-toolsets.git
synced 2026-04-16 14:46:24 +02:00
2870 lines
146 KiB
HTML
2870 lines
146 KiB
HTML
|
|
<!doctype html>
|
|
<html lang="en" class="no-js">
|
|
<head>
|
|
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
|
|
<meta name="description" content="Production-ready utilities for FastAPI applications.">
|
|
|
|
|
|
<meta name="author" content="d3vyce">
|
|
|
|
|
|
<link rel="canonical" href="https://fastapi-toolsets.d3vyce.fr/v3.0/module/crud/">
|
|
|
|
|
|
<link rel="prev" href="../cli/">
|
|
|
|
|
|
<link rel="next" href="../db/">
|
|
|
|
|
|
|
|
|
|
|
|
<link rel="icon" href="../../assets/images/favicon.png">
|
|
<meta name="generator" content="zensical-0.0.31">
|
|
|
|
|
|
|
|
<title>CRUD - FastAPI Toolsets</title>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<link rel="stylesheet" href="../../assets/stylesheets/modern/main.53a7feaf.min.css">
|
|
|
|
|
|
|
|
|
|
<link rel="stylesheet" href="../../assets/stylesheets/modern/palette.dfe2e883.min.css">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,500,500i,700,700i%7CJetbrains+Mono:400,400i,700,700i&display=fallback">
|
|
<style>:root{--md-text-font:"Inter";--md-code-font:"Jetbrains Mono"}</style>
|
|
|
|
|
|
|
|
<script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,t)=>(e<<5)-e+t.charCodeAt(0)),0),__md_get=(e,t=localStorage,a=__md_scope)=>JSON.parse(t.getItem(a.pathname+"."+e)),__md_set=(e,t,a=localStorage,_=__md_scope)=>{try{a.setItem(_.pathname+"."+e,JSON.stringify(t))}catch(e){}},document.documentElement.setAttribute("data-platform",navigator.platform)</script>
|
|
|
|
|
|
|
|
|
|
|
|
<script
|
|
defer
|
|
src="https://analytics.d3vyce.fr/script.js"
|
|
data-website-id="338b8816-7b99-4c6a-82f3-15595be3fd47"
|
|
></script>
|
|
|
|
</head>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="indigo">
|
|
|
|
|
|
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
|
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
|
<label class="md-overlay" for="__drawer" aria-label="Navigation"></label>
|
|
<div data-md-component="skip">
|
|
|
|
|
|
<a href="#crud" class="md-skip">
|
|
Skip to content
|
|
</a>
|
|
|
|
</div>
|
|
<div data-md-component="announce">
|
|
|
|
</div>
|
|
|
|
<div data-md-color-scheme="default" data-md-component="outdated" hidden>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<header class="md-header" data-md-component="header">
|
|
<nav class="md-header__inner md-grid" aria-label="Header">
|
|
<a href="../.." title="FastAPI Toolsets" class="md-header__button md-logo" aria-label="FastAPI Toolsets" data-md-component="logo">
|
|
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-book-open" viewBox="0 0 24 24"><path d="M12 7v14M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
|
|
|
|
</a>
|
|
<label class="md-header__button md-icon" for="__drawer" aria-label="Navigation">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-menu" viewBox="0 0 24 24"><path d="M4 5h16M4 12h16M4 19h16"/></svg>
|
|
</label>
|
|
<div class="md-header__title" data-md-component="header-title">
|
|
<div class="md-header__ellipsis">
|
|
<div class="md-header__topic">
|
|
<span class="md-ellipsis">
|
|
FastAPI Toolsets
|
|
</span>
|
|
</div>
|
|
<div class="md-header__topic" data-md-component="header-topic">
|
|
<span class="md-ellipsis">
|
|
|
|
CRUD
|
|
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<form class="md-header__option" data-md-component="palette">
|
|
|
|
|
|
|
|
|
|
<input class="md-option" data-md-color-media="none" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="indigo" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_0">
|
|
|
|
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_1" hidden>
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/></svg>
|
|
</label>
|
|
|
|
|
|
|
|
|
|
|
|
<input class="md-option" data-md-color-media="none" data-md-color-scheme="slate" data-md-color-primary="indigo" data-md-color-accent="indigo" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_1">
|
|
|
|
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_0" hidden>
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-moon" viewBox="0 0 24 24"><path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"/></svg>
|
|
</label>
|
|
|
|
|
|
</form>
|
|
|
|
|
|
|
|
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
|
|
|
|
|
|
|
|
|
|
|
<label class="md-header__button md-icon" for="__search" aria-label="Search">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-search" viewBox="0 0 24 24"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>
|
|
</label>
|
|
<div class="md-search" data-md-component="search" role="dialog" aria-label="Search">
|
|
<button type="button" class="md-search__button">
|
|
Search
|
|
</button>
|
|
</div>
|
|
|
|
|
|
<div class="md-header__source">
|
|
|
|
<a href="https://github.com/d3vyce/fastapi-toolsets" title="Go to repository" class="md-source" data-md-component="source">
|
|
<div class="md-source__icon md-icon">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2026 Fonticons, Inc.--><path fill="currentColor" d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
|
|
</div>
|
|
<div class="md-source__repository">
|
|
GitHub
|
|
</div>
|
|
</a>
|
|
|
|
</div>
|
|
</nav>
|
|
|
|
</header>
|
|
|
|
<div class="md-container" data-md-component="container">
|
|
|
|
|
|
|
|
|
|
|
|
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
|
<div class="md-grid">
|
|
<ul class="md-tabs__list">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../.." class="md-tabs__link">
|
|
|
|
|
|
Home
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item md-tabs__item--active">
|
|
<a href="../cli/" class="md-tabs__link">
|
|
|
|
|
|
Modules
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../../reference/cli/" class="md-tabs__link">
|
|
|
|
|
|
Reference
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../../examples/pagination-search/" class="md-tabs__link">
|
|
|
|
|
|
Examples
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../../migration/v3/" class="md-tabs__link">
|
|
|
|
|
|
Migration
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="https://github.com/d3vyce/fastapi-toolsets/releases" class="md-tabs__link">
|
|
|
|
|
|
Changelog ↗
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
|
|
|
|
<main class="md-main" data-md-component="main">
|
|
<div class="md-main__inner md-grid">
|
|
|
|
|
|
|
|
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
|
<div class="md-sidebar__scrollwrap">
|
|
<div class="md-sidebar__inner">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
|
|
<label class="md-nav__title" for="__drawer">
|
|
<a href="../.." title="FastAPI Toolsets" class="md-nav__button md-logo" aria-label="FastAPI Toolsets" data-md-component="logo">
|
|
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-book-open" viewBox="0 0 24 24"><path d="M12 7v14M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
|
|
|
|
</a>
|
|
FastAPI Toolsets
|
|
</label>
|
|
|
|
<div class="md-nav__source">
|
|
<a href="https://github.com/d3vyce/fastapi-toolsets" title="Go to repository" class="md-source" data-md-component="source">
|
|
<div class="md-source__icon md-icon">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2026 Fonticons, Inc.--><path fill="currentColor" d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
|
|
</div>
|
|
<div class="md-source__repository">
|
|
GitHub
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../.." class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Home
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
|
|
|
|
|
|
|
|
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2" checked>
|
|
|
|
|
|
<label class="md-nav__link" for="__nav_2" id="__nav_2_label" tabindex="">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Modules
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<span class="md-nav__icon md-icon"></span>
|
|
</label>
|
|
|
|
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="true">
|
|
<label class="md-nav__title" for="__nav_2">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
|
|
|
|
Modules
|
|
|
|
|
|
</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../cli/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
CLI
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--active">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
CRUD
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<span class="md-nav__icon md-icon"></span>
|
|
</label>
|
|
|
|
<a href="././" class="md-nav__link md-nav__link--active">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
CRUD
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
<nav class="md-nav md-nav--secondary" aria-label="On this page">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<label class="md-nav__title" for="__toc">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
On this page
|
|
</label>
|
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#overview" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Overview
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#creating-a-crud-class" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Creating a CRUD class
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Creating a CRUD class">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#factory-style" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Factory style
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#subclass-style" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Subclass style
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#adding-custom-methods" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Adding custom methods
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#sharing-a-custom-base-across-multiple-models" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Sharing a custom base across multiple models
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#basic-operations" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Basic operations
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#fetching-a-single-record" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Fetching a single record
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#pagination" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Pagination
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Pagination">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#offset-pagination" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Offset pagination
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Offset pagination">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#skipping-the-count-query" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Skipping the COUNT query
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#cursor-pagination" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Cursor pagination
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Cursor pagination">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#choosing-a-cursor-column" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Choosing a cursor column
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#unified-endpoint-both-strategies" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Unified endpoint (both strategies)
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#search" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Search
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Search">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#full-text-search" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Full-text search
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#faceted-search" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Faceted search
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#sorting" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Sorting
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#relationship-loading" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Relationship loading
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#many-to-many-relationships" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Many-to-many relationships
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#upsert" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Upsert
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#response-serialization" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Response serialization
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../db/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Database
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../dependencies/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Dependencies
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../exceptions/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Exceptions
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../fixtures/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Fixtures
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../logger/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Logger
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../metrics/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Metrics
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../models/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Models
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../pytest/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Pytest
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../schemas/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Schemas
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--nested">
|
|
|
|
|
|
|
|
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3" >
|
|
|
|
|
|
<label class="md-nav__link" for="__nav_3" id="__nav_3_label" tabindex="0">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Reference
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<span class="md-nav__icon md-icon"></span>
|
|
</label>
|
|
|
|
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_3_label" aria-expanded="false">
|
|
<label class="md-nav__title" for="__nav_3">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
|
|
|
|
Reference
|
|
|
|
|
|
</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../reference/cli/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
CLI
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../reference/crud/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
CRUD
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../reference/db/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Database
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../reference/dependencies/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Dependencies
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../reference/exceptions/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Exceptions
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../reference/fixtures/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Fixtures
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../reference/logger/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Logger
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../reference/metrics/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Metrics
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../reference/models/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Models
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../reference/pytest/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Pytest
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../reference/schemas/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Schemas
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--nested">
|
|
|
|
|
|
|
|
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" >
|
|
|
|
|
|
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Examples
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<span class="md-nav__icon md-icon"></span>
|
|
</label>
|
|
|
|
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
|
|
<label class="md-nav__title" for="__nav_4">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
|
|
|
|
Examples
|
|
|
|
|
|
</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../examples/pagination-search/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Pagination & Search
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--nested">
|
|
|
|
|
|
|
|
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5" >
|
|
|
|
|
|
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="0">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Migration
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<span class="md-nav__icon md-icon"></span>
|
|
</label>
|
|
|
|
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false">
|
|
<label class="md-nav__title" for="__nav_5">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
|
|
|
|
Migration
|
|
|
|
|
|
</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../migration/v3/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
v3.0
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../migration/v2/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
v2.0
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="https://github.com/d3vyce/fastapi-toolsets/releases" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Changelog ↗
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
|
<div class="md-sidebar__scrollwrap">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
|
|
<div class="md-sidebar-button__wrapper">
|
|
<label class="md-sidebar-button" for="__toc"></label>
|
|
</div>
|
|
|
|
|
|
<div class="md-sidebar__inner">
|
|
|
|
|
|
|
|
<nav class="md-nav md-nav--secondary" aria-label="On this page">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<label class="md-nav__title" for="__toc">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
On this page
|
|
</label>
|
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#overview" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Overview
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#creating-a-crud-class" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Creating a CRUD class
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Creating a CRUD class">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#factory-style" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Factory style
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#subclass-style" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Subclass style
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#adding-custom-methods" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Adding custom methods
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#sharing-a-custom-base-across-multiple-models" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Sharing a custom base across multiple models
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#basic-operations" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Basic operations
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#fetching-a-single-record" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Fetching a single record
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#pagination" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Pagination
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Pagination">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#offset-pagination" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Offset pagination
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Offset pagination">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#skipping-the-count-query" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Skipping the COUNT query
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#cursor-pagination" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Cursor pagination
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Cursor pagination">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#choosing-a-cursor-column" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Choosing a cursor column
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#unified-endpoint-both-strategies" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Unified endpoint (both strategies)
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#search" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Search
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Search">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#full-text-search" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Full-text search
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#faceted-search" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Faceted search
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#sorting" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Sorting
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#relationship-loading" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Relationship loading
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#many-to-many-relationships" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Many-to-many relationships
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#upsert" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Upsert
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#response-serialization" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Response serialization
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="md-content" data-md-component="content">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<nav class="md-path" aria-label="Navigation" >
|
|
<ol class="md-path__list">
|
|
|
|
|
|
|
|
|
|
<li class="md-path__item">
|
|
<a href="../.." class="md-path__link">
|
|
|
|
<span class="md-ellipsis">
|
|
Home
|
|
</span>
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-path__item">
|
|
<a href="../cli/" class="md-path__link">
|
|
|
|
<span class="md-ellipsis">
|
|
Modules
|
|
</span>
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
</ol>
|
|
</nav>
|
|
|
|
|
|
<article class="md-content__inner md-typeset">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<a href="https://github.com/d3vyce/fastapi-toolsets/raw/master/docs/module/crud.md" title="View source of this page" class="md-content__button md-icon">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-file-code-2" viewBox="0 0 24 24"><path d="M4 12.15V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-3.35"/><path d="M14 2v5a1 1 0 0 0 1 1h5M5 16l-3 3 3 3M9 22l3-3-3-3"/></svg>
|
|
</a>
|
|
|
|
|
|
|
|
<h1 id="crud">CRUD<a class="headerlink" href="#crud" title="Permanent link">¶</a></h1>
|
|
<p>Generic async CRUD operations for SQLAlchemy models with search, pagination, and many-to-many support.</p>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">Info</p>
|
|
<p>This module has been coded and tested to be compatible with PostgreSQL only.</p>
|
|
</div>
|
|
<h2 id="overview">Overview<a class="headerlink" href="#overview" title="Permanent link">¶</a></h2>
|
|
<p>The <code>crud</code> module provides <a href="../../reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud"><code>AsyncCrud</code></a>, a base class with a full suite of async database operations, and <a href="../../reference/crud/#fastapi_toolsets.crud.factory.CrudFactory"><code>CrudFactory</code></a>, a convenience function to instantiate it for a given model.</p>
|
|
<h2 id="creating-a-crud-class">Creating a CRUD class<a class="headerlink" href="#creating-a-crud-class" title="Permanent link">¶</a></h2>
|
|
<h3 id="factory-style">Factory style<a class="headerlink" href="#factory-style" title="Permanent link">¶</a></h3>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.crud</span><span class="w"> </span><span class="kn">import</span> <span class="n">CrudFactory</span>
|
|
</span><span id="__span-0-2"><a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">myapp.models</span><span class="w"> </span><span class="kn">import</span> <span class="n">User</span>
|
|
</span><span id="__span-0-3"><a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>
|
|
</span><span id="__span-0-4"><a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="n">UserCrud</span> <span class="o">=</span> <span class="n">CrudFactory</span><span class="p">(</span><span class="n">model</span><span class="o">=</span><span class="n">User</span><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<p><a href="../../reference/crud/#fastapi_toolsets.crud.factory.CrudFactory"><code>CrudFactory</code></a> dynamically creates a class named <code>AsyncUserCrud</code> with <code>User</code> as its model. This is the most concise option for straightforward CRUD with no custom logic.</p>
|
|
<h3 id="subclass-style">Subclass style<a class="headerlink" href="#subclass-style" title="Permanent link">¶</a></h3>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">Added in <code>v2.3.0</code></p>
|
|
</div>
|
|
<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">fastapi_toolsets.crud.factory</span><span class="w"> </span><span class="kn">import</span> <span class="n">AsyncCrud</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">myapp.models</span><span class="w"> </span><span class="kn">import</span> <span class="n">User</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">UserCrud</span><span class="p">(</span><span class="n">AsyncCrud</span><span class="p">[</span><span class="n">User</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">model</span> <span class="o">=</span> <span class="n">User</span>
|
|
</span><span id="__span-1-6"><a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a> <span class="n">searchable_fields</span> <span class="o">=</span> <span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">username</span><span class="p">,</span> <span class="n">User</span><span class="o">.</span><span class="n">email</span><span class="p">]</span>
|
|
</span><span id="__span-1-7"><a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a> <span class="n">default_load_options</span> <span class="o">=</span> <span class="p">[</span><span class="n">selectinload</span><span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">role</span><span class="p">)]</span>
|
|
</span></code></pre></div>
|
|
<p>Subclassing <a href="../../reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud"><code>AsyncCrud</code></a> directly is the preferred style when you need to add custom methods or when the configuration is complex enough to benefit from a named class body.</p>
|
|
<h3 id="adding-custom-methods">Adding custom methods<a class="headerlink" href="#adding-custom-methods" title="Permanent link">¶</a></h3>
|
|
<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">class</span><span class="w"> </span><span class="nc">UserCrud</span><span class="p">(</span><span class="n">AsyncCrud</span><span class="p">[</span><span class="n">User</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="n">model</span> <span class="o">=</span> <span class="n">User</span>
|
|
</span><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a>
|
|
</span><span id="__span-2-4"><a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a> <span class="nd">@classmethod</span>
|
|
</span><span id="__span-2-5"><a id="__codelineno-2-5" name="__codelineno-2-5" href="#__codelineno-2-5"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_active</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">session</span><span class="p">:</span> <span class="n">AsyncSession</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="n">User</span><span class="p">]:</span>
|
|
</span><span id="__span-2-6"><a id="__codelineno-2-6" name="__codelineno-2-6" href="#__codelineno-2-6"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">cls</span><span class="o">.</span><span class="n">get_multi</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">filters</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">is_active</span> <span class="o">==</span> <span class="kc">True</span><span class="p">])</span>
|
|
</span></code></pre></div>
|
|
<h3 id="sharing-a-custom-base-across-multiple-models">Sharing a custom base across multiple models<a class="headerlink" href="#sharing-a-custom-base-across-multiple-models" title="Permanent link">¶</a></h3>
|
|
<p>Define a generic base class with the shared methods, then subclass it for each model:</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">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Generic</span><span class="p">,</span> <span class="n">TypeVar</span>
|
|
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">sqlalchemy.ext.asyncio</span><span class="w"> </span><span class="kn">import</span> <span class="n">AsyncSession</span>
|
|
</span><span id="__span-3-3"><a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">sqlalchemy.orm</span><span class="w"> </span><span class="kn">import</span> <span class="n">DeclarativeBase</span>
|
|
</span><span id="__span-3-4"><a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.crud.factory</span><span class="w"> </span><span class="kn">import</span> <span class="n">AsyncCrud</span>
|
|
</span><span id="__span-3-5"><a id="__codelineno-3-5" name="__codelineno-3-5" href="#__codelineno-3-5"></a>
|
|
</span><span id="__span-3-6"><a id="__codelineno-3-6" name="__codelineno-3-6" href="#__codelineno-3-6"></a><span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">"T"</span><span class="p">,</span> <span class="n">bound</span><span class="o">=</span><span class="n">DeclarativeBase</span><span class="p">)</span>
|
|
</span><span id="__span-3-7"><a id="__codelineno-3-7" name="__codelineno-3-7" href="#__codelineno-3-7"></a>
|
|
</span><span id="__span-3-8"><a id="__codelineno-3-8" name="__codelineno-3-8" href="#__codelineno-3-8"></a><span class="k">class</span><span class="w"> </span><span class="nc">AuditedCrud</span><span class="p">(</span><span class="n">AsyncCrud</span><span class="p">[</span><span class="n">T</span><span class="p">],</span> <span class="n">Generic</span><span class="p">[</span><span class="n">T</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="w"> </span><span class="sd">"""Base CRUD with custom function"""</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="nd">@classmethod</span>
|
|
</span><span id="__span-3-12"><a id="__codelineno-3-12" name="__codelineno-3-12" href="#__codelineno-3-12"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_active</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">session</span><span class="p">:</span> <span class="n">AsyncSession</span><span class="p">):</span>
|
|
</span><span id="__span-3-13"><a id="__codelineno-3-13" name="__codelineno-3-13" href="#__codelineno-3-13"></a> <span class="k">return</span> <span class="k">await</span> <span class="bp">cls</span><span class="o">.</span><span class="n">get_multi</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">filters</span><span class="o">=</span><span class="p">[</span><span class="bp">cls</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">is_active</span> <span class="o">==</span> <span class="kc">True</span><span class="p">])</span>
|
|
</span><span id="__span-3-14"><a id="__codelineno-3-14" name="__codelineno-3-14" href="#__codelineno-3-14"></a>
|
|
</span><span id="__span-3-15"><a id="__codelineno-3-15" name="__codelineno-3-15" href="#__codelineno-3-15"></a>
|
|
</span><span id="__span-3-16"><a id="__codelineno-3-16" name="__codelineno-3-16" href="#__codelineno-3-16"></a><span class="k">class</span><span class="w"> </span><span class="nc">UserCrud</span><span class="p">(</span><span class="n">AuditedCrud</span><span class="p">[</span><span class="n">User</span><span class="p">]):</span>
|
|
</span><span id="__span-3-17"><a id="__codelineno-3-17" name="__codelineno-3-17" href="#__codelineno-3-17"></a> <span class="n">model</span> <span class="o">=</span> <span class="n">User</span>
|
|
</span><span id="__span-3-18"><a id="__codelineno-3-18" name="__codelineno-3-18" href="#__codelineno-3-18"></a> <span class="n">searchable_fields</span> <span class="o">=</span> <span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">username</span><span class="p">,</span> <span class="n">User</span><span class="o">.</span><span class="n">email</span><span class="p">]</span>
|
|
</span></code></pre></div>
|
|
<p>You can also use the factory shorthand with the same base by passing <code>base_class</code>:</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">UserCrud</span> <span class="o">=</span> <span class="n">CrudFactory</span><span class="p">(</span><span class="n">User</span><span class="p">,</span> <span class="n">base_class</span><span class="o">=</span><span class="n">AuditedCrud</span><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<h2 id="basic-operations">Basic operations<a class="headerlink" href="#basic-operations" title="Permanent link">¶</a></h2>
|
|
<div class="admonition info">
|
|
<p class="admonition-title"><code>get_or_none</code> added in <code>v2.2</code></p>
|
|
</div>
|
|
<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="c1"># Create</span>
|
|
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a><span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">create</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">obj</span><span class="o">=</span><span class="n">UserCreateSchema</span><span class="p">(</span><span class="n">username</span><span class="o">=</span><span class="s2">"alice"</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><span id="__span-5-4"><a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a><span class="c1"># Get one (raises NotFoundError if not found)</span>
|
|
</span><span id="__span-5-5"><a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a><span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">get</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">filters</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="n">user_id</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><span id="__span-5-7"><a id="__codelineno-5-7" name="__codelineno-5-7" href="#__codelineno-5-7"></a><span class="c1"># Get one or None (never raises)</span>
|
|
</span><span id="__span-5-8"><a id="__codelineno-5-8" name="__codelineno-5-8" href="#__codelineno-5-8"></a><span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">get_or_none</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">filters</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="n">user_id</span><span class="p">])</span>
|
|
</span><span id="__span-5-9"><a id="__codelineno-5-9" name="__codelineno-5-9" href="#__codelineno-5-9"></a>
|
|
</span><span id="__span-5-10"><a id="__codelineno-5-10" name="__codelineno-5-10" href="#__codelineno-5-10"></a><span class="c1"># Get first or None</span>
|
|
</span><span id="__span-5-11"><a id="__codelineno-5-11" name="__codelineno-5-11" href="#__codelineno-5-11"></a><span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">first</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">filters</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">email</span> <span class="o">==</span> <span class="n">email</span><span class="p">])</span>
|
|
</span><span id="__span-5-12"><a id="__codelineno-5-12" name="__codelineno-5-12" href="#__codelineno-5-12"></a>
|
|
</span><span id="__span-5-13"><a id="__codelineno-5-13" name="__codelineno-5-13" href="#__codelineno-5-13"></a><span class="c1"># Get multiple</span>
|
|
</span><span id="__span-5-14"><a id="__codelineno-5-14" name="__codelineno-5-14" href="#__codelineno-5-14"></a><span class="n">users</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">get_multi</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">filters</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">is_active</span> <span class="o">==</span> <span class="kc">True</span><span class="p">])</span>
|
|
</span><span id="__span-5-15"><a id="__codelineno-5-15" name="__codelineno-5-15" href="#__codelineno-5-15"></a>
|
|
</span><span id="__span-5-16"><a id="__codelineno-5-16" name="__codelineno-5-16" href="#__codelineno-5-16"></a><span class="c1"># Update</span>
|
|
</span><span id="__span-5-17"><a id="__codelineno-5-17" name="__codelineno-5-17" href="#__codelineno-5-17"></a><span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">update</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">obj</span><span class="o">=</span><span class="n">UserUpdateSchema</span><span class="p">(</span><span class="n">username</span><span class="o">=</span><span class="s2">"bob"</span><span class="p">),</span> <span class="n">filters</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="n">user_id</span><span class="p">])</span>
|
|
</span><span id="__span-5-18"><a id="__codelineno-5-18" name="__codelineno-5-18" href="#__codelineno-5-18"></a>
|
|
</span><span id="__span-5-19"><a id="__codelineno-5-19" name="__codelineno-5-19" href="#__codelineno-5-19"></a><span class="c1"># Delete</span>
|
|
</span><span id="__span-5-20"><a id="__codelineno-5-20" name="__codelineno-5-20" href="#__codelineno-5-20"></a><span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">delete</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">filters</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="n">user_id</span><span class="p">])</span>
|
|
</span><span id="__span-5-21"><a id="__codelineno-5-21" name="__codelineno-5-21" href="#__codelineno-5-21"></a>
|
|
</span><span id="__span-5-22"><a id="__codelineno-5-22" name="__codelineno-5-22" href="#__codelineno-5-22"></a><span class="c1"># Count / exists</span>
|
|
</span><span id="__span-5-23"><a id="__codelineno-5-23" name="__codelineno-5-23" href="#__codelineno-5-23"></a><span class="n">count</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">count</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">filters</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">is_active</span> <span class="o">==</span> <span class="kc">True</span><span class="p">])</span>
|
|
</span><span id="__span-5-24"><a id="__codelineno-5-24" name="__codelineno-5-24" href="#__codelineno-5-24"></a><span class="n">exists</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">exists</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">filters</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">email</span> <span class="o">==</span> <span class="n">email</span><span class="p">])</span>
|
|
</span></code></pre></div>
|
|
<h2 id="fetching-a-single-record">Fetching a single record<a class="headerlink" href="#fetching-a-single-record" title="Permanent link">¶</a></h2>
|
|
<p>Three methods fetch a single record — choose based on how you want to handle the "not found" case and whether you need strict uniqueness:</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Method</th>
|
|
<th>Not found</th>
|
|
<th>Multiple results</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>get</code></td>
|
|
<td>raises <code>NotFoundError</code></td>
|
|
<td>raises <code>MultipleResultsFound</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>get_or_none</code></td>
|
|
<td>returns <code>None</code></td>
|
|
<td>raises <code>MultipleResultsFound</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>first</code></td>
|
|
<td>returns <code>None</code></td>
|
|
<td>returns the first match silently</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p>Use <code>get</code> when the record must exist (e.g. a detail endpoint that should return 404):</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="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">get</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">filters</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="n">user_id</span><span class="p">])</span>
|
|
</span></code></pre></div>
|
|
<p>Use <code>get_or_none</code> when the record may not exist but you still want strict uniqueness enforcement:</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="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">get_or_none</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">filters</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">email</span> <span class="o">==</span> <span class="n">email</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="k">if</span> <span class="n">user</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
|
</span><span id="__span-7-3"><a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a> <span class="o">...</span> <span class="c1"># handle missing case without catching an exception</span>
|
|
</span></code></pre></div>
|
|
<p>Use <code>first</code> when you only care about any one match and don't need uniqueness:</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="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">first</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">filters</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">is_active</span> <span class="o">==</span> <span class="kc">True</span><span class="p">])</span>
|
|
</span></code></pre></div>
|
|
<h2 id="pagination">Pagination<a class="headerlink" href="#pagination" title="Permanent link">¶</a></h2>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">Added in <code>v1.1</code> (only offset_pagination via <code>paginate</code> if <code><v1.1</code>)</p>
|
|
</div>
|
|
<p>Three pagination methods are available. All return a typed response whose <code>pagination_type</code> field tells clients which strategy was used.</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th></th>
|
|
<th><code>offset_paginate</code></th>
|
|
<th><code>cursor_paginate</code></th>
|
|
<th><code>paginate</code></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>Return type</td>
|
|
<td><code>OffsetPaginatedResponse</code></td>
|
|
<td><code>CursorPaginatedResponse</code></td>
|
|
<td>either, based on <code>pagination_type</code> param</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Total count</td>
|
|
<td>Yes</td>
|
|
<td>No</td>
|
|
<td>/</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Jump to arbitrary page</td>
|
|
<td>Yes</td>
|
|
<td>No</td>
|
|
<td>/</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Performance on deep pages</td>
|
|
<td>Degrades</td>
|
|
<td>Constant</td>
|
|
<td>/</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Stable under concurrent inserts</td>
|
|
<td>No</td>
|
|
<td>Yes</td>
|
|
<td>/</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Use case</td>
|
|
<td>Admin panels, numbered pagination</td>
|
|
<td>Feeds, APIs, infinite scroll</td>
|
|
<td>single endpoint, both strategies</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<h3 id="offset-pagination">Offset pagination<a class="headerlink" href="#offset-pagination" title="Permanent link">¶</a></h3>
|
|
<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">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Annotated</span>
|
|
</span><span id="__span-9-2"><a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">Depends</span>
|
|
</span><span id="__span-9-3"><a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a>
|
|
</span><span id="__span-9-4"><a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a><span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">""</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="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_users</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">session</span><span class="p">:</span> <span class="n">SessionDep</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">params</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">dict</span><span class="p">,</span> <span class="n">Depends</span><span class="p">(</span><span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate_params</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="p">)</span> <span class="o">-></span> <span class="n">OffsetPaginatedResponse</span><span class="p">[</span><span class="n">UserRead</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="k">return</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate</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="o">**</span><span class="n">params</span><span class="p">,</span> <span class="n">schema</span><span class="o">=</span><span class="n">UserRead</span><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<p>The <a href="../../reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.offset_paginate"><code>offset_paginate</code></a> method returns an <a href="../../reference/schemas/#fastapi_toolsets.schemas.OffsetPaginatedResponse"><code>OffsetPaginatedResponse</code></a>:</p>
|
|
<div class="language-json 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="p">{</span>
|
|
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"SUCCESS"</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="w"> </span><span class="nt">"pagination_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"offset"</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="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"..."</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="w"> </span><span class="nt">"pagination"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
|
</span><span id="__span-10-6"><a id="__codelineno-10-6" name="__codelineno-10-6" href="#__codelineno-10-6"></a><span class="w"> </span><span class="nt">"total_count"</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="p">,</span>
|
|
</span><span id="__span-10-7"><a id="__codelineno-10-7" name="__codelineno-10-7" href="#__codelineno-10-7"></a><span class="w"> </span><span class="nt">"pages"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span>
|
|
</span><span id="__span-10-8"><a id="__codelineno-10-8" name="__codelineno-10-8" href="#__codelineno-10-8"></a><span class="w"> </span><span class="nt">"page"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
|
</span><span id="__span-10-9"><a id="__codelineno-10-9" name="__codelineno-10-9" href="#__codelineno-10-9"></a><span class="w"> </span><span class="nt">"items_per_page"</span><span class="p">:</span><span class="w"> </span><span class="mi">20</span><span class="p">,</span>
|
|
</span><span id="__span-10-10"><a id="__codelineno-10-10" name="__codelineno-10-10" href="#__codelineno-10-10"></a><span class="w"> </span><span class="nt">"has_more"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span>
|
|
</span><span id="__span-10-11"><a id="__codelineno-10-11" name="__codelineno-10-11" href="#__codelineno-10-11"></a><span class="w"> </span><span class="p">}</span>
|
|
</span><span id="__span-10-12"><a id="__codelineno-10-12" name="__codelineno-10-12" href="#__codelineno-10-12"></a><span class="p">}</span>
|
|
</span></code></pre></div>
|
|
<h4 id="skipping-the-count-query">Skipping the COUNT query<a class="headerlink" href="#skipping-the-count-query" title="Permanent link">¶</a></h4>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">Added in <code>v2.4.1</code></p>
|
|
</div>
|
|
<p>By default <code>offset_paginate</code> runs two queries: one for the page items and one <code>COUNT(*)</code> for <code>total_count</code>. On large tables the <code>COUNT</code> can be expensive. Pass <code>include_total=False</code> to <code>offset_paginate_params()</code> to skip it:</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="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">""</span><span class="p">)</span>
|
|
</span><span id="__span-11-2"><a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_users</span><span class="p">(</span>
|
|
</span><span id="__span-11-3"><a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a> <span class="n">session</span><span class="p">:</span> <span class="n">SessionDep</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">params</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">dict</span><span class="p">,</span> <span class="n">Depends</span><span class="p">(</span><span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate_params</span><span class="p">(</span><span class="n">include_total</span><span class="o">=</span><span class="kc">False</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="p">)</span> <span class="o">-></span> <span class="n">OffsetPaginatedResponse</span><span class="p">[</span><span class="n">UserRead</span><span class="p">]:</span>
|
|
</span><span id="__span-11-6"><a id="__codelineno-11-6" name="__codelineno-11-6" href="#__codelineno-11-6"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate</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="o">**</span><span class="n">params</span><span class="p">,</span> <span class="n">schema</span><span class="o">=</span><span class="n">UserRead</span><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<h3 id="cursor-pagination">Cursor pagination<a class="headerlink" href="#cursor-pagination" title="Permanent link">¶</a></h3>
|
|
<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="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">""</span><span class="p">)</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">list_users</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="n">session</span><span class="p">:</span> <span class="n">SessionDep</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">params</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">dict</span><span class="p">,</span> <span class="n">Depends</span><span class="p">(</span><span class="n">UserCrud</span><span class="o">.</span><span class="n">cursor_paginate_params</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="p">)</span> <span class="o">-></span> <span class="n">CursorPaginatedResponse</span><span class="p">[</span><span class="n">UserRead</span><span class="p">]:</span>
|
|
</span><span id="__span-12-6"><a id="__codelineno-12-6" name="__codelineno-12-6" href="#__codelineno-12-6"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">cursor_paginate</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="o">**</span><span class="n">params</span><span class="p">,</span> <span class="n">schema</span><span class="o">=</span><span class="n">UserRead</span><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<p>The <a href="../../reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.cursor_paginate"><code>cursor_paginate</code></a> method returns a <a href="../../reference/schemas/#fastapi_toolsets.schemas.CursorPaginatedResponse"><code>CursorPaginatedResponse</code></a>:</p>
|
|
<div class="language-json 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="p">{</span>
|
|
</span><span id="__span-13-2"><a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"SUCCESS"</span><span class="p">,</span>
|
|
</span><span id="__span-13-3"><a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a><span class="w"> </span><span class="nt">"pagination_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cursor"</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="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"..."</span><span class="p">],</span>
|
|
</span><span id="__span-13-5"><a id="__codelineno-13-5" name="__codelineno-13-5" href="#__codelineno-13-5"></a><span class="w"> </span><span class="nt">"pagination"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
|
</span><span id="__span-13-6"><a id="__codelineno-13-6" name="__codelineno-13-6" href="#__codelineno-13-6"></a><span class="w"> </span><span class="nt">"next_cursor"</span><span class="p">:</span><span class="w"> </span><span class="s2">"eyJ2YWx1ZSI6ICIzZjQ3YWM2OS0uLi4ifQ=="</span><span class="p">,</span>
|
|
</span><span id="__span-13-7"><a id="__codelineno-13-7" name="__codelineno-13-7" href="#__codelineno-13-7"></a><span class="w"> </span><span class="nt">"prev_cursor"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span>
|
|
</span><span id="__span-13-8"><a id="__codelineno-13-8" name="__codelineno-13-8" href="#__codelineno-13-8"></a><span class="w"> </span><span class="nt">"items_per_page"</span><span class="p">:</span><span class="w"> </span><span class="mi">20</span><span class="p">,</span>
|
|
</span><span id="__span-13-9"><a id="__codelineno-13-9" name="__codelineno-13-9" href="#__codelineno-13-9"></a><span class="w"> </span><span class="nt">"has_more"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span>
|
|
</span><span id="__span-13-10"><a id="__codelineno-13-10" name="__codelineno-13-10" href="#__codelineno-13-10"></a><span class="w"> </span><span class="p">}</span>
|
|
</span><span id="__span-13-11"><a id="__codelineno-13-11" name="__codelineno-13-11" href="#__codelineno-13-11"></a><span class="p">}</span>
|
|
</span></code></pre></div>
|
|
<p>Pass <code>next_cursor</code> as the <code>cursor</code> query parameter on the next request to advance to the next page. <code>prev_cursor</code> is set on pages 2+ and points back to the first item of the current page. Both are <code>null</code> when there is no adjacent page.</p>
|
|
<h4 id="choosing-a-cursor-column">Choosing a cursor column<a class="headerlink" href="#choosing-a-cursor-column" title="Permanent link">¶</a></h4>
|
|
<p>The cursor column is set once on <a href="../../reference/crud/#fastapi_toolsets.crud.factory.CrudFactory"><code>CrudFactory</code></a> via the <code>cursor_column</code> parameter. It must be monotonically ordered for stable results:</p>
|
|
<ul>
|
|
<li>Auto-increment integer PKs</li>
|
|
<li>UUID v7 PKs</li>
|
|
<li>Timestamps</li>
|
|
</ul>
|
|
<div class="admonition warning">
|
|
<p class="admonition-title">Warning</p>
|
|
<p>Random UUID v4 PKs are <strong>not</strong> suitable as cursor columns because their ordering is non-deterministic.</p>
|
|
</div>
|
|
<div class="admonition note">
|
|
<p class="admonition-title">Note</p>
|
|
<p><code>cursor_column</code> is required. Calling <a href="../../reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.cursor_paginate"><code>cursor_paginate</code></a> on a CRUD class that has no <code>cursor_column</code> configured raises a <code>ValueError</code>.</p>
|
|
</div>
|
|
<p>The cursor value is URL-safe base64-encoded (no padding) when returned to the client and decoded back to the correct Python type on the next request. The following SQLAlchemy column types are supported:</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>SQLAlchemy type</th>
|
|
<th>Python type</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>Integer</code>, <code>BigInteger</code>, <code>SmallInteger</code></td>
|
|
<td><code>int</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Uuid</code></td>
|
|
<td><code>uuid.UUID</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>DateTime</code></td>
|
|
<td><code>datetime.datetime</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Date</code></td>
|
|
<td><code>datetime.date</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Float</code>, <code>Numeric</code></td>
|
|
<td><code>decimal.Decimal</code></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<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="c1"># Paginate by the primary key</span>
|
|
</span><span id="__span-14-2"><a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a><span class="n">PostCrud</span> <span class="o">=</span> <span class="n">CrudFactory</span><span class="p">(</span><span class="n">model</span><span class="o">=</span><span class="n">Post</span><span class="p">,</span> <span class="n">cursor_column</span><span class="o">=</span><span class="n">Post</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
|
|
</span><span id="__span-14-3"><a id="__codelineno-14-3" name="__codelineno-14-3" href="#__codelineno-14-3"></a>
|
|
</span><span id="__span-14-4"><a id="__codelineno-14-4" name="__codelineno-14-4" href="#__codelineno-14-4"></a><span class="c1"># Paginate by a timestamp column instead</span>
|
|
</span><span id="__span-14-5"><a id="__codelineno-14-5" name="__codelineno-14-5" href="#__codelineno-14-5"></a><span class="n">PostCrud</span> <span class="o">=</span> <span class="n">CrudFactory</span><span class="p">(</span><span class="n">model</span><span class="o">=</span><span class="n">Post</span><span class="p">,</span> <span class="n">cursor_column</span><span class="o">=</span><span class="n">Post</span><span class="o">.</span><span class="n">created_at</span><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<h3 id="unified-endpoint-both-strategies">Unified endpoint (both strategies)<a class="headerlink" href="#unified-endpoint-both-strategies" title="Permanent link">¶</a></h3>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">Added in <code>v2.3.0</code></p>
|
|
</div>
|
|
<p><a href="../../reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.paginate"><code>paginate()</code></a> dispatches to <code>offset_paginate</code> or <code>cursor_paginate</code> based on a <code>pagination_type</code> query parameter, letting you expose <strong>one endpoint</strong> that supports both strategies. The <code>pagination_type</code> field in the response tells clients which strategy was used, enabling frontend discriminated-union typing.</p>
|
|
<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.schemas</span><span class="w"> </span><span class="kn">import</span> <span class="n">PaginatedResponse</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="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">""</span><span class="p">)</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">def</span><span class="w"> </span><span class="nf">list_users</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="n">session</span><span class="p">:</span> <span class="n">SessionDep</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 class="n">params</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">dict</span><span class="p">,</span> <span class="n">Depends</span><span class="p">(</span><span class="n">UserCrud</span><span class="o">.</span><span class="n">paginate_params</span><span class="p">())],</span>
|
|
</span><span id="__span-15-7"><a id="__codelineno-15-7" name="__codelineno-15-7" href="#__codelineno-15-7"></a><span class="p">)</span> <span class="o">-></span> <span class="n">PaginatedResponse</span><span class="p">[</span><span class="n">UserRead</span><span class="p">]:</span>
|
|
</span><span id="__span-15-8"><a id="__codelineno-15-8" name="__codelineno-15-8" href="#__codelineno-15-8"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">paginate</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="o">**</span><span class="n">params</span><span class="p">,</span> <span class="n">schema</span><span class="o">=</span><span class="n">UserRead</span><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<div class="language-text highlight"><pre><span></span><code><span id="__span-16-1"><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a>GET /users?pagination_type=offset&page=2&items_per_page=10
|
|
</span><span id="__span-16-2"><a id="__codelineno-16-2" name="__codelineno-16-2" href="#__codelineno-16-2"></a>GET /users?pagination_type=cursor&cursor=eyJ2YWx1ZSI6...&items_per_page=10
|
|
</span></code></pre></div>
|
|
<h2 id="search">Search<a class="headerlink" href="#search" title="Permanent link">¶</a></h2>
|
|
<p>Two search strategies are available, both compatible with <a href="../../reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.offset_paginate"><code>offset_paginate</code></a> and <a href="../../reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.cursor_paginate"><code>cursor_paginate</code></a>.</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th></th>
|
|
<th>Full-text search</th>
|
|
<th>Faceted search</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>Input</td>
|
|
<td>Free-text string</td>
|
|
<td>Exact column values</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Relationship support</td>
|
|
<td>Yes</td>
|
|
<td>Yes</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Use case</td>
|
|
<td>Search bars</td>
|
|
<td>Filter dropdowns</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">You can use both search strategies in the same endpoint!</p>
|
|
</div>
|
|
<h3 id="full-text-search">Full-text search<a class="headerlink" href="#full-text-search" title="Permanent link">¶</a></h3>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">Added in <code>v2.2.1</code></p>
|
|
<p>The model's primary key is always included in <code>searchable_fields</code> automatically, so searching by ID works out of the box without any configuration. When no <code>searchable_fields</code> are declared, only the primary key is searched.</p>
|
|
</div>
|
|
<p>Declare <code>searchable_fields</code> on the CRUD class. Relationship traversal is supported via tuples:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-17-1"><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-1"></a><span class="n">PostCrud</span> <span class="o">=</span> <span class="n">CrudFactory</span><span class="p">(</span>
|
|
</span><span id="__span-17-2"><a id="__codelineno-17-2" name="__codelineno-17-2" href="#__codelineno-17-2"></a> <span class="n">model</span><span class="o">=</span><span class="n">Post</span><span class="p">,</span>
|
|
</span><span id="__span-17-3"><a id="__codelineno-17-3" name="__codelineno-17-3" href="#__codelineno-17-3"></a> <span class="n">searchable_fields</span><span class="o">=</span><span class="p">[</span>
|
|
</span><span id="__span-17-4"><a id="__codelineno-17-4" name="__codelineno-17-4" href="#__codelineno-17-4"></a> <span class="n">Post</span><span class="o">.</span><span class="n">title</span><span class="p">,</span>
|
|
</span><span id="__span-17-5"><a id="__codelineno-17-5" name="__codelineno-17-5" href="#__codelineno-17-5"></a> <span class="n">Post</span><span class="o">.</span><span class="n">content</span><span class="p">,</span>
|
|
</span><span id="__span-17-6"><a id="__codelineno-17-6" name="__codelineno-17-6" href="#__codelineno-17-6"></a> <span class="p">(</span><span class="n">Post</span><span class="o">.</span><span class="n">author</span><span class="p">,</span> <span class="n">User</span><span class="o">.</span><span class="n">username</span><span class="p">),</span> <span class="c1"># search across relationship</span>
|
|
</span><span id="__span-17-7"><a id="__codelineno-17-7" name="__codelineno-17-7" href="#__codelineno-17-7"></a> <span class="p">],</span>
|
|
</span><span id="__span-17-8"><a id="__codelineno-17-8" name="__codelineno-17-8" href="#__codelineno-17-8"></a><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<p>You can override <code>searchable_fields</code> per call with <code>search_fields</code>:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-18-1"><a id="__codelineno-18-1" name="__codelineno-18-1" href="#__codelineno-18-1"></a><span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate</span><span class="p">(</span>
|
|
</span><span id="__span-18-2"><a id="__codelineno-18-2" name="__codelineno-18-2" href="#__codelineno-18-2"></a> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span>
|
|
</span><span id="__span-18-3"><a id="__codelineno-18-3" name="__codelineno-18-3" href="#__codelineno-18-3"></a> <span class="n">search_fields</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">country</span><span class="p">],</span>
|
|
</span><span id="__span-18-4"><a id="__codelineno-18-4" name="__codelineno-18-4" href="#__codelineno-18-4"></a><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<p>Or via the dependency to narrow which fields are exposed as query parameters:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-19-1"><a id="__codelineno-19-1" name="__codelineno-19-1" href="#__codelineno-19-1"></a><span class="n">params</span> <span class="o">=</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate_params</span><span class="p">(</span><span class="n">search_fields</span><span class="o">=</span><span class="p">[</span><span class="n">Post</span><span class="o">.</span><span class="n">title</span><span class="p">])</span>
|
|
</span></code></pre></div>
|
|
<p>This allows searching with both <a href="../../reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.offset_paginate"><code>offset_paginate</code></a> and <a href="../../reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.cursor_paginate"><code>cursor_paginate</code></a>:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-20-1"><a id="__codelineno-20-1" name="__codelineno-20-1" href="#__codelineno-20-1"></a><span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">""</span><span class="p">)</span>
|
|
</span><span id="__span-20-2"><a id="__codelineno-20-2" name="__codelineno-20-2" href="#__codelineno-20-2"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_users</span><span class="p">(</span>
|
|
</span><span id="__span-20-3"><a id="__codelineno-20-3" name="__codelineno-20-3" href="#__codelineno-20-3"></a> <span class="n">session</span><span class="p">:</span> <span class="n">SessionDep</span><span class="p">,</span>
|
|
</span><span id="__span-20-4"><a id="__codelineno-20-4" name="__codelineno-20-4" href="#__codelineno-20-4"></a> <span class="n">params</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">dict</span><span class="p">,</span> <span class="n">Depends</span><span class="p">(</span><span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate_params</span><span class="p">())],</span>
|
|
</span><span id="__span-20-5"><a id="__codelineno-20-5" name="__codelineno-20-5" href="#__codelineno-20-5"></a><span class="p">)</span> <span class="o">-></span> <span class="n">OffsetPaginatedResponse</span><span class="p">[</span><span class="n">UserRead</span><span class="p">]:</span>
|
|
</span><span id="__span-20-6"><a id="__codelineno-20-6" name="__codelineno-20-6" href="#__codelineno-20-6"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate</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="o">**</span><span class="n">params</span><span class="p">,</span> <span class="n">schema</span><span class="o">=</span><span class="n">UserRead</span><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-21-1"><a id="__codelineno-21-1" name="__codelineno-21-1" href="#__codelineno-21-1"></a><span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">""</span><span class="p">)</span>
|
|
</span><span id="__span-21-2"><a id="__codelineno-21-2" name="__codelineno-21-2" href="#__codelineno-21-2"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_users</span><span class="p">(</span>
|
|
</span><span id="__span-21-3"><a id="__codelineno-21-3" name="__codelineno-21-3" href="#__codelineno-21-3"></a> <span class="n">session</span><span class="p">:</span> <span class="n">SessionDep</span><span class="p">,</span>
|
|
</span><span id="__span-21-4"><a id="__codelineno-21-4" name="__codelineno-21-4" href="#__codelineno-21-4"></a> <span class="n">params</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">dict</span><span class="p">,</span> <span class="n">Depends</span><span class="p">(</span><span class="n">UserCrud</span><span class="o">.</span><span class="n">cursor_paginate_params</span><span class="p">())],</span>
|
|
</span><span id="__span-21-5"><a id="__codelineno-21-5" name="__codelineno-21-5" href="#__codelineno-21-5"></a><span class="p">)</span> <span class="o">-></span> <span class="n">CursorPaginatedResponse</span><span class="p">[</span><span class="n">UserRead</span><span class="p">]:</span>
|
|
</span><span id="__span-21-6"><a id="__codelineno-21-6" name="__codelineno-21-6" href="#__codelineno-21-6"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">cursor_paginate</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="o">**</span><span class="n">params</span><span class="p">,</span> <span class="n">schema</span><span class="o">=</span><span class="n">UserRead</span><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<p>The dependency adds two query parameters to the endpoint:</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Parameter</th>
|
|
<th>Type</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>search</code></td>
|
|
<td><code>str \| null</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>search_column</code></td>
|
|
<td><code>str \| null</code></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<div class="language-text highlight"><pre><span></span><code><span id="__span-22-1"><a id="__codelineno-22-1" name="__codelineno-22-1" href="#__codelineno-22-1"></a>GET /posts?search=hello → search all configured columns
|
|
</span><span id="__span-22-2"><a id="__codelineno-22-2" name="__codelineno-22-2" href="#__codelineno-22-2"></a>GET /posts?search=hello&search_column=title → search only Post.title
|
|
</span></code></pre></div>
|
|
<p>The available search column keys are returned in the <code>search_columns</code> field of <a href="../../reference/schemas/#fastapi_toolsets.schemas.PaginatedResponse"><code>PaginatedResponse</code></a>. Use them to populate a column picker in the UI, or to validate <code>search_column</code> values on the client side:</p>
|
|
<div class="language-json highlight"><pre><span></span><code><span id="__span-23-1"><a id="__codelineno-23-1" name="__codelineno-23-1" href="#__codelineno-23-1"></a><span class="p">{</span>
|
|
</span><span id="__span-23-2"><a id="__codelineno-23-2" name="__codelineno-23-2" href="#__codelineno-23-2"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"SUCCESS"</span><span class="p">,</span>
|
|
</span><span id="__span-23-3"><a id="__codelineno-23-3" name="__codelineno-23-3" href="#__codelineno-23-3"></a><span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"..."</span><span class="p">],</span>
|
|
</span><span id="__span-23-4"><a id="__codelineno-23-4" name="__codelineno-23-4" href="#__codelineno-23-4"></a><span class="w"> </span><span class="nt">"pagination"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s2">"..."</span><span class="w"> </span><span class="p">},</span>
|
|
</span><span id="__span-23-5"><a id="__codelineno-23-5" name="__codelineno-23-5" href="#__codelineno-23-5"></a><span class="w"> </span><span class="nt">"search_columns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"content"</span><span class="p">,</span><span class="w"> </span><span class="s2">"author__username"</span><span class="p">,</span><span class="w"> </span><span class="s2">"title"</span><span class="p">]</span>
|
|
</span><span id="__span-23-6"><a id="__codelineno-23-6" name="__codelineno-23-6" href="#__codelineno-23-6"></a><span class="p">}</span>
|
|
</span></code></pre></div>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">Key format uses <code>__</code> as a separator for relationship chains.</p>
|
|
<p>A direct column <code>Post.title</code> produces <code>"title"</code>. A relationship tuple <code>(Post.author, User.username)</code> produces <code>"author__username"</code>. An unknown <code>search_column</code> value raises <a href="../../reference/exceptions/#fastapi_toolsets.exceptions.exceptions.InvalidSearchColumnError"><code>InvalidSearchColumnError</code></a> (HTTP 422).</p>
|
|
</div>
|
|
<h3 id="faceted-search">Faceted search<a class="headerlink" href="#faceted-search" title="Permanent link">¶</a></h3>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">Added in <code>v1.2</code></p>
|
|
</div>
|
|
<p>Declare <code>facet_fields</code> on the CRUD class to return distinct column values alongside paginated results. This is useful for populating filter dropdowns or building faceted search UIs. Relationship traversal is supported via tuples, using the same syntax as <code>searchable_fields</code>:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-24-1"><a id="__codelineno-24-1" name="__codelineno-24-1" href="#__codelineno-24-1"></a><span class="n">UserCrud</span> <span class="o">=</span> <span class="n">CrudFactory</span><span class="p">(</span>
|
|
</span><span id="__span-24-2"><a id="__codelineno-24-2" name="__codelineno-24-2" href="#__codelineno-24-2"></a> <span class="n">model</span><span class="o">=</span><span class="n">User</span><span class="p">,</span>
|
|
</span><span id="__span-24-3"><a id="__codelineno-24-3" name="__codelineno-24-3" href="#__codelineno-24-3"></a> <span class="n">facet_fields</span><span class="o">=</span><span class="p">[</span>
|
|
</span><span id="__span-24-4"><a id="__codelineno-24-4" name="__codelineno-24-4" href="#__codelineno-24-4"></a> <span class="n">User</span><span class="o">.</span><span class="n">status</span><span class="p">,</span>
|
|
</span><span id="__span-24-5"><a id="__codelineno-24-5" name="__codelineno-24-5" href="#__codelineno-24-5"></a> <span class="n">User</span><span class="o">.</span><span class="n">country</span><span class="p">,</span>
|
|
</span><span id="__span-24-6"><a id="__codelineno-24-6" name="__codelineno-24-6" href="#__codelineno-24-6"></a> <span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">role</span><span class="p">,</span> <span class="n">Role</span><span class="o">.</span><span class="n">name</span><span class="p">),</span> <span class="c1"># value from a related model</span>
|
|
</span><span id="__span-24-7"><a id="__codelineno-24-7" name="__codelineno-24-7" href="#__codelineno-24-7"></a> <span class="p">],</span>
|
|
</span><span id="__span-24-8"><a id="__codelineno-24-8" name="__codelineno-24-8" href="#__codelineno-24-8"></a><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<p>You can override <code>facet_fields</code> per call:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-25-1"><a id="__codelineno-25-1" name="__codelineno-25-1" href="#__codelineno-25-1"></a><span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate</span><span class="p">(</span>
|
|
</span><span id="__span-25-2"><a id="__codelineno-25-2" name="__codelineno-25-2" href="#__codelineno-25-2"></a> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span>
|
|
</span><span id="__span-25-3"><a id="__codelineno-25-3" name="__codelineno-25-3" href="#__codelineno-25-3"></a> <span class="n">facet_fields</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">country</span><span class="p">],</span>
|
|
</span><span id="__span-25-4"><a id="__codelineno-25-4" name="__codelineno-25-4" href="#__codelineno-25-4"></a><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<p>Or via the dependency to narrow which fields are exposed as query parameters:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-26-1"><a id="__codelineno-26-1" name="__codelineno-26-1" href="#__codelineno-26-1"></a><span class="n">params</span> <span class="o">=</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate_params</span><span class="p">(</span><span class="n">facet_fields</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">country</span><span class="p">])</span>
|
|
</span></code></pre></div>
|
|
<p>Facet filtering is built into the consolidated params dependencies. When <code>filter=True</code> (the default), each facet field is exposed as a query parameter and values are collected into <code>filter_by</code> automatically:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-27-1"><a id="__codelineno-27-1" name="__codelineno-27-1" href="#__codelineno-27-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Annotated</span>
|
|
</span><span id="__span-27-2"><a id="__codelineno-27-2" name="__codelineno-27-2" href="#__codelineno-27-2"></a>
|
|
</span><span id="__span-27-3"><a id="__codelineno-27-3" name="__codelineno-27-3" href="#__codelineno-27-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">Depends</span>
|
|
</span><span id="__span-27-4"><a id="__codelineno-27-4" name="__codelineno-27-4" href="#__codelineno-27-4"></a>
|
|
</span><span id="__span-27-5"><a id="__codelineno-27-5" name="__codelineno-27-5" href="#__codelineno-27-5"></a><span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">""</span><span class="p">,</span> <span class="n">response_model_exclude_none</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
|
</span><span id="__span-27-6"><a id="__codelineno-27-6" name="__codelineno-27-6" href="#__codelineno-27-6"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_users</span><span class="p">(</span>
|
|
</span><span id="__span-27-7"><a id="__codelineno-27-7" name="__codelineno-27-7" href="#__codelineno-27-7"></a> <span class="n">session</span><span class="p">:</span> <span class="n">SessionDep</span><span class="p">,</span>
|
|
</span><span id="__span-27-8"><a id="__codelineno-27-8" name="__codelineno-27-8" href="#__codelineno-27-8"></a> <span class="n">params</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">dict</span><span class="p">,</span> <span class="n">Depends</span><span class="p">(</span><span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate_params</span><span class="p">())],</span>
|
|
</span><span id="__span-27-9"><a id="__codelineno-27-9" name="__codelineno-27-9" href="#__codelineno-27-9"></a><span class="p">)</span> <span class="o">-></span> <span class="n">OffsetPaginatedResponse</span><span class="p">[</span><span class="n">UserRead</span><span class="p">]:</span>
|
|
</span><span id="__span-27-10"><a id="__codelineno-27-10" name="__codelineno-27-10" href="#__codelineno-27-10"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate</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="o">**</span><span class="n">params</span><span class="p">,</span> <span class="n">schema</span><span class="o">=</span><span class="n">UserRead</span><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-28-1"><a id="__codelineno-28-1" name="__codelineno-28-1" href="#__codelineno-28-1"></a><span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">""</span><span class="p">,</span> <span class="n">response_model_exclude_none</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
|
</span><span id="__span-28-2"><a id="__codelineno-28-2" name="__codelineno-28-2" href="#__codelineno-28-2"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_users</span><span class="p">(</span>
|
|
</span><span id="__span-28-3"><a id="__codelineno-28-3" name="__codelineno-28-3" href="#__codelineno-28-3"></a> <span class="n">session</span><span class="p">:</span> <span class="n">SessionDep</span><span class="p">,</span>
|
|
</span><span id="__span-28-4"><a id="__codelineno-28-4" name="__codelineno-28-4" href="#__codelineno-28-4"></a> <span class="n">params</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">dict</span><span class="p">,</span> <span class="n">Depends</span><span class="p">(</span><span class="n">UserCrud</span><span class="o">.</span><span class="n">cursor_paginate_params</span><span class="p">())],</span>
|
|
</span><span id="__span-28-5"><a id="__codelineno-28-5" name="__codelineno-28-5" href="#__codelineno-28-5"></a><span class="p">)</span> <span class="o">-></span> <span class="n">CursorPaginatedResponse</span><span class="p">[</span><span class="n">UserRead</span><span class="p">]:</span>
|
|
</span><span id="__span-28-6"><a id="__codelineno-28-6" name="__codelineno-28-6" href="#__codelineno-28-6"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">cursor_paginate</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="o">**</span><span class="n">params</span><span class="p">,</span> <span class="n">schema</span><span class="o">=</span><span class="n">UserRead</span><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<p>Both single-value and multi-value query parameters work:</p>
|
|
<div class="language-text highlight"><pre><span></span><code><span id="__span-29-1"><a id="__codelineno-29-1" name="__codelineno-29-1" href="#__codelineno-29-1"></a>GET /users?status=active → filter_by={"status": ["active"]}
|
|
</span><span id="__span-29-2"><a id="__codelineno-29-2" name="__codelineno-29-2" href="#__codelineno-29-2"></a>GET /users?status=active&country=FR → filter_by={"status": ["active"], "country": ["FR"]}
|
|
</span><span id="__span-29-3"><a id="__codelineno-29-3" name="__codelineno-29-3" href="#__codelineno-29-3"></a>GET /users?role__name=admin&role__name=editor → filter_by={"role__name": ["admin", "editor"]} (IN clause)
|
|
</span></code></pre></div>
|
|
<p><code>filter_by</code> and <code>filters</code> can be combined — both are applied with AND logic.</p>
|
|
<p>The distinct values for each facet field are returned in the <code>filter_attributes</code> field of <a href="../../reference/schemas/#fastapi_toolsets.schemas.PaginatedResponse"><code>PaginatedResponse</code></a>. Use them to populate filter dropdowns in the UI, or to validate <code>filter_by</code> keys on the client side:</p>
|
|
<div class="language-json highlight"><pre><span></span><code><span id="__span-30-1"><a id="__codelineno-30-1" name="__codelineno-30-1" href="#__codelineno-30-1"></a><span class="p">{</span>
|
|
</span><span id="__span-30-2"><a id="__codelineno-30-2" name="__codelineno-30-2" href="#__codelineno-30-2"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"SUCCESS"</span><span class="p">,</span>
|
|
</span><span id="__span-30-3"><a id="__codelineno-30-3" name="__codelineno-30-3" href="#__codelineno-30-3"></a><span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"..."</span><span class="p">],</span>
|
|
</span><span id="__span-30-4"><a id="__codelineno-30-4" name="__codelineno-30-4" href="#__codelineno-30-4"></a><span class="w"> </span><span class="nt">"pagination"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s2">"..."</span><span class="w"> </span><span class="p">},</span>
|
|
</span><span id="__span-30-5"><a id="__codelineno-30-5" name="__codelineno-30-5" href="#__codelineno-30-5"></a><span class="w"> </span><span class="nt">"filter_attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
|
</span><span id="__span-30-6"><a id="__codelineno-30-6" name="__codelineno-30-6" href="#__codelineno-30-6"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"active"</span><span class="p">,</span><span class="w"> </span><span class="s2">"inactive"</span><span class="p">],</span>
|
|
</span><span id="__span-30-7"><a id="__codelineno-30-7" name="__codelineno-30-7" href="#__codelineno-30-7"></a><span class="w"> </span><span class="nt">"country"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"DE"</span><span class="p">,</span><span class="w"> </span><span class="s2">"FR"</span><span class="p">,</span><span class="w"> </span><span class="s2">"US"</span><span class="p">],</span>
|
|
</span><span id="__span-30-8"><a id="__codelineno-30-8" name="__codelineno-30-8" href="#__codelineno-30-8"></a><span class="w"> </span><span class="nt">"role__name"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"admin"</span><span class="p">,</span><span class="w"> </span><span class="s2">"editor"</span><span class="p">,</span><span class="w"> </span><span class="s2">"viewer"</span><span class="p">]</span>
|
|
</span><span id="__span-30-9"><a id="__codelineno-30-9" name="__codelineno-30-9" href="#__codelineno-30-9"></a><span class="w"> </span><span class="p">}</span>
|
|
</span><span id="__span-30-10"><a id="__codelineno-30-10" name="__codelineno-30-10" href="#__codelineno-30-10"></a><span class="p">}</span>
|
|
</span></code></pre></div>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">Key format uses <code>__</code> as a separator for relationship chains.</p>
|
|
<p>A direct column <code>User.status</code> produces <code>"status"</code>. A relationship tuple <code>(User.role, Role.name)</code> produces <code>"role__name"</code>. A deeper chain <code>(User.role, Role.permission, Permission.name)</code> produces <code>"role__permission__name"</code>. An unknown <code>filter_by</code> key raises <a href="../../reference/exceptions/#fastapi_toolsets.exceptions.exceptions.InvalidFacetFilterError"><code>InvalidFacetFilterError</code></a> (HTTP 422).</p>
|
|
</div>
|
|
<h2 id="sorting">Sorting<a class="headerlink" href="#sorting" title="Permanent link">¶</a></h2>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">Added in <code>v1.3</code></p>
|
|
</div>
|
|
<p>Declare <code>order_fields</code> on the CRUD class. Relationship traversal is supported via tuples, using the same syntax as <code>searchable_fields</code> and <code>facet_fields</code>:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-31-1"><a id="__codelineno-31-1" name="__codelineno-31-1" href="#__codelineno-31-1"></a><span class="n">UserCrud</span> <span class="o">=</span> <span class="n">CrudFactory</span><span class="p">(</span>
|
|
</span><span id="__span-31-2"><a id="__codelineno-31-2" name="__codelineno-31-2" href="#__codelineno-31-2"></a> <span class="n">model</span><span class="o">=</span><span class="n">User</span><span class="p">,</span>
|
|
</span><span id="__span-31-3"><a id="__codelineno-31-3" name="__codelineno-31-3" href="#__codelineno-31-3"></a> <span class="n">order_fields</span><span class="o">=</span><span class="p">[</span>
|
|
</span><span id="__span-31-4"><a id="__codelineno-31-4" name="__codelineno-31-4" href="#__codelineno-31-4"></a> <span class="n">User</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
|
|
</span><span id="__span-31-5"><a id="__codelineno-31-5" name="__codelineno-31-5" href="#__codelineno-31-5"></a> <span class="n">User</span><span class="o">.</span><span class="n">created_at</span><span class="p">,</span>
|
|
</span><span id="__span-31-6"><a id="__codelineno-31-6" name="__codelineno-31-6" href="#__codelineno-31-6"></a> <span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">role</span><span class="p">,</span> <span class="n">Role</span><span class="o">.</span><span class="n">name</span><span class="p">),</span> <span class="c1"># sort by a related model column</span>
|
|
</span><span id="__span-31-7"><a id="__codelineno-31-7" name="__codelineno-31-7" href="#__codelineno-31-7"></a> <span class="p">],</span>
|
|
</span><span id="__span-31-8"><a id="__codelineno-31-8" name="__codelineno-31-8" href="#__codelineno-31-8"></a><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<p>You can override <code>order_fields</code> per call:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-32-1"><a id="__codelineno-32-1" name="__codelineno-32-1" href="#__codelineno-32-1"></a><span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate</span><span class="p">(</span>
|
|
</span><span id="__span-32-2"><a id="__codelineno-32-2" name="__codelineno-32-2" href="#__codelineno-32-2"></a> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span>
|
|
</span><span id="__span-32-3"><a id="__codelineno-32-3" name="__codelineno-32-3" href="#__codelineno-32-3"></a> <span class="n">order_fields</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">name</span><span class="p">],</span>
|
|
</span><span id="__span-32-4"><a id="__codelineno-32-4" name="__codelineno-32-4" href="#__codelineno-32-4"></a><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<p>Or via the dependency to narrow which fields are exposed as query parameters:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-33-1"><a id="__codelineno-33-1" name="__codelineno-33-1" href="#__codelineno-33-1"></a><span class="n">params</span> <span class="o">=</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate_params</span><span class="p">(</span><span class="n">order_fields</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">name</span><span class="p">])</span>
|
|
</span></code></pre></div>
|
|
<p>Sorting is built into the consolidated params dependencies. When <code>order=True</code> (the default), <code>order_by</code> and <code>order</code> query parameters are exposed and resolved into an <code>OrderByClause</code> automatically:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-34-1"><a id="__codelineno-34-1" name="__codelineno-34-1" href="#__codelineno-34-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Annotated</span>
|
|
</span><span id="__span-34-2"><a id="__codelineno-34-2" name="__codelineno-34-2" href="#__codelineno-34-2"></a>
|
|
</span><span id="__span-34-3"><a id="__codelineno-34-3" name="__codelineno-34-3" href="#__codelineno-34-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">Depends</span>
|
|
</span><span id="__span-34-4"><a id="__codelineno-34-4" name="__codelineno-34-4" href="#__codelineno-34-4"></a>
|
|
</span><span id="__span-34-5"><a id="__codelineno-34-5" name="__codelineno-34-5" href="#__codelineno-34-5"></a><span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">""</span><span class="p">)</span>
|
|
</span><span id="__span-34-6"><a id="__codelineno-34-6" name="__codelineno-34-6" href="#__codelineno-34-6"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_users</span><span class="p">(</span>
|
|
</span><span id="__span-34-7"><a id="__codelineno-34-7" name="__codelineno-34-7" href="#__codelineno-34-7"></a> <span class="n">session</span><span class="p">:</span> <span class="n">SessionDep</span><span class="p">,</span>
|
|
</span><span id="__span-34-8"><a id="__codelineno-34-8" name="__codelineno-34-8" href="#__codelineno-34-8"></a> <span class="n">params</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">dict</span><span class="p">,</span> <span class="n">Depends</span><span class="p">(</span><span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate_params</span><span class="p">())],</span>
|
|
</span><span id="__span-34-9"><a id="__codelineno-34-9" name="__codelineno-34-9" href="#__codelineno-34-9"></a><span class="p">)</span> <span class="o">-></span> <span class="n">OffsetPaginatedResponse</span><span class="p">[</span><span class="n">UserRead</span><span class="p">]:</span>
|
|
</span><span id="__span-34-10"><a id="__codelineno-34-10" name="__codelineno-34-10" href="#__codelineno-34-10"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate</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="o">**</span><span class="n">params</span><span class="p">,</span> <span class="n">schema</span><span class="o">=</span><span class="n">UserRead</span><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-35-1"><a id="__codelineno-35-1" name="__codelineno-35-1" href="#__codelineno-35-1"></a><span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">""</span><span class="p">)</span>
|
|
</span><span id="__span-35-2"><a id="__codelineno-35-2" name="__codelineno-35-2" href="#__codelineno-35-2"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_users</span><span class="p">(</span>
|
|
</span><span id="__span-35-3"><a id="__codelineno-35-3" name="__codelineno-35-3" href="#__codelineno-35-3"></a> <span class="n">session</span><span class="p">:</span> <span class="n">SessionDep</span><span class="p">,</span>
|
|
</span><span id="__span-35-4"><a id="__codelineno-35-4" name="__codelineno-35-4" href="#__codelineno-35-4"></a> <span class="n">params</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">dict</span><span class="p">,</span> <span class="n">Depends</span><span class="p">(</span><span class="n">UserCrud</span><span class="o">.</span><span class="n">cursor_paginate_params</span><span class="p">())],</span>
|
|
</span><span id="__span-35-5"><a id="__codelineno-35-5" name="__codelineno-35-5" href="#__codelineno-35-5"></a><span class="p">)</span> <span class="o">-></span> <span class="n">CursorPaginatedResponse</span><span class="p">[</span><span class="n">UserRead</span><span class="p">]:</span>
|
|
</span><span id="__span-35-6"><a id="__codelineno-35-6" name="__codelineno-35-6" href="#__codelineno-35-6"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">cursor_paginate</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="o">**</span><span class="n">params</span><span class="p">,</span> <span class="n">schema</span><span class="o">=</span><span class="n">UserRead</span><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<p>The dependency adds two query parameters to the endpoint:</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Parameter</th>
|
|
<th>Type</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>order_by</code></td>
|
|
<td><code>str \| null</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>order</code></td>
|
|
<td><code>asc</code> or <code>desc</code></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<div class="language-text highlight"><pre><span></span><code><span id="__span-36-1"><a id="__codelineno-36-1" name="__codelineno-36-1" href="#__codelineno-36-1"></a>GET /users?order_by=name&order=asc → ORDER BY users.name ASC
|
|
</span><span id="__span-36-2"><a id="__codelineno-36-2" name="__codelineno-36-2" href="#__codelineno-36-2"></a>GET /users?order_by=role__name&order=desc → LEFT JOIN roles ON ... ORDER BY roles.name DESC
|
|
</span></code></pre></div>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">Relationship tuples are joined automatically.</p>
|
|
<p>When a relation field is selected, the related table is LEFT OUTER JOINed automatically. An unknown <code>order_by</code> value raises <a href="../../reference/exceptions/#fastapi_toolsets.exceptions.exceptions.InvalidOrderFieldError"><code>InvalidOrderFieldError</code></a> (HTTP 422).</p>
|
|
</div>
|
|
<p>The available sort keys are returned in the <code>order_columns</code> field of <a href="../../reference/schemas/#fastapi_toolsets.schemas.PaginatedResponse"><code>PaginatedResponse</code></a>. Use them to populate a sort picker in the UI, or to validate <code>order_by</code> values on the client side:</p>
|
|
<div class="language-json highlight"><pre><span></span><code><span id="__span-37-1"><a id="__codelineno-37-1" name="__codelineno-37-1" href="#__codelineno-37-1"></a><span class="p">{</span>
|
|
</span><span id="__span-37-2"><a id="__codelineno-37-2" name="__codelineno-37-2" href="#__codelineno-37-2"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"SUCCESS"</span><span class="p">,</span>
|
|
</span><span id="__span-37-3"><a id="__codelineno-37-3" name="__codelineno-37-3" href="#__codelineno-37-3"></a><span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"..."</span><span class="p">],</span>
|
|
</span><span id="__span-37-4"><a id="__codelineno-37-4" name="__codelineno-37-4" href="#__codelineno-37-4"></a><span class="w"> </span><span class="nt">"pagination"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s2">"..."</span><span class="w"> </span><span class="p">},</span>
|
|
</span><span id="__span-37-5"><a id="__codelineno-37-5" name="__codelineno-37-5" href="#__codelineno-37-5"></a><span class="w"> </span><span class="nt">"order_columns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"created_at"</span><span class="p">,</span><span class="w"> </span><span class="s2">"name"</span><span class="p">,</span><span class="w"> </span><span class="s2">"role__name"</span><span class="p">]</span>
|
|
</span><span id="__span-37-6"><a id="__codelineno-37-6" name="__codelineno-37-6" href="#__codelineno-37-6"></a><span class="p">}</span>
|
|
</span></code></pre></div>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">Key format uses <code>__</code> as a separator for relationship chains.</p>
|
|
<p>A direct column <code>User.name</code> produces <code>"name"</code>. A relationship tuple <code>(User.role, Role.name)</code> produces <code>"role__name"</code>.</p>
|
|
</div>
|
|
<h2 id="relationship-loading">Relationship loading<a class="headerlink" href="#relationship-loading" title="Permanent link">¶</a></h2>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">Added in <code>v1.1</code></p>
|
|
</div>
|
|
<p>By default, SQLAlchemy relationships are not loaded unless explicitly requested. Instead of using <code>lazy="selectin"</code> on model definitions (which is implicit and applies globally), define a <code>default_load_options</code> on the CRUD class to control loading strategy explicitly.</p>
|
|
<div class="admonition warning">
|
|
<p class="admonition-title">Warning</p>
|
|
<p>Avoid using <code>lazy="selectin"</code> on model relationships. It fires silently on every query, cannot be disabled per-call, and can cause unexpected cascading loads through deep relationship chains. Use <code>default_load_options</code> instead.</p>
|
|
</div>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-38-1"><a id="__codelineno-38-1" name="__codelineno-38-1" href="#__codelineno-38-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">sqlalchemy.orm</span><span class="w"> </span><span class="kn">import</span> <span class="n">selectinload</span>
|
|
</span><span id="__span-38-2"><a id="__codelineno-38-2" name="__codelineno-38-2" href="#__codelineno-38-2"></a>
|
|
</span><span id="__span-38-3"><a id="__codelineno-38-3" name="__codelineno-38-3" href="#__codelineno-38-3"></a><span class="n">ArticleCrud</span> <span class="o">=</span> <span class="n">CrudFactory</span><span class="p">(</span>
|
|
</span><span id="__span-38-4"><a id="__codelineno-38-4" name="__codelineno-38-4" href="#__codelineno-38-4"></a> <span class="n">model</span><span class="o">=</span><span class="n">Article</span><span class="p">,</span>
|
|
</span><span id="__span-38-5"><a id="__codelineno-38-5" name="__codelineno-38-5" href="#__codelineno-38-5"></a> <span class="n">default_load_options</span><span class="o">=</span><span class="p">[</span>
|
|
</span><span id="__span-38-6"><a id="__codelineno-38-6" name="__codelineno-38-6" href="#__codelineno-38-6"></a> <span class="n">selectinload</span><span class="p">(</span><span class="n">Article</span><span class="o">.</span><span class="n">category</span><span class="p">),</span>
|
|
</span><span id="__span-38-7"><a id="__codelineno-38-7" name="__codelineno-38-7" href="#__codelineno-38-7"></a> <span class="n">selectinload</span><span class="p">(</span><span class="n">Article</span><span class="o">.</span><span class="n">tags</span><span class="p">),</span>
|
|
</span><span id="__span-38-8"><a id="__codelineno-38-8" name="__codelineno-38-8" href="#__codelineno-38-8"></a> <span class="p">],</span>
|
|
</span><span id="__span-38-9"><a id="__codelineno-38-9" name="__codelineno-38-9" href="#__codelineno-38-9"></a><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<p><code>default_load_options</code> applies automatically to all read operations (<code>get</code>, <code>first</code>, <code>get_multi</code>, <code>offset_paginate</code>, <code>cursor_paginate</code>). When <code>load_options</code> is passed at call-site, it <strong>fully replaces</strong> <code>default_load_options</code> for that query — giving you precise per-call control:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-39-1"><a id="__codelineno-39-1" name="__codelineno-39-1" href="#__codelineno-39-1"></a><span class="c1"># Only loads category, tags are not loaded</span>
|
|
</span><span id="__span-39-2"><a id="__codelineno-39-2" name="__codelineno-39-2" href="#__codelineno-39-2"></a><span class="n">article</span> <span class="o">=</span> <span class="k">await</span> <span class="n">ArticleCrud</span><span class="o">.</span><span class="n">get</span><span class="p">(</span>
|
|
</span><span id="__span-39-3"><a id="__codelineno-39-3" name="__codelineno-39-3" href="#__codelineno-39-3"></a> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span>
|
|
</span><span id="__span-39-4"><a id="__codelineno-39-4" name="__codelineno-39-4" href="#__codelineno-39-4"></a> <span class="n">filters</span><span class="o">=</span><span class="p">[</span><span class="n">Article</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="n">article_id</span><span class="p">],</span>
|
|
</span><span id="__span-39-5"><a id="__codelineno-39-5" name="__codelineno-39-5" href="#__codelineno-39-5"></a> <span class="n">load_options</span><span class="o">=</span><span class="p">[</span><span class="n">selectinload</span><span class="p">(</span><span class="n">Article</span><span class="o">.</span><span class="n">category</span><span class="p">)],</span>
|
|
</span><span id="__span-39-6"><a id="__codelineno-39-6" name="__codelineno-39-6" href="#__codelineno-39-6"></a><span class="p">)</span>
|
|
</span><span id="__span-39-7"><a id="__codelineno-39-7" name="__codelineno-39-7" href="#__codelineno-39-7"></a>
|
|
</span><span id="__span-39-8"><a id="__codelineno-39-8" name="__codelineno-39-8" href="#__codelineno-39-8"></a><span class="c1"># Loads nothing — useful for write-then-refresh flows or lightweight checks</span>
|
|
</span><span id="__span-39-9"><a id="__codelineno-39-9" name="__codelineno-39-9" href="#__codelineno-39-9"></a><span class="n">articles</span> <span class="o">=</span> <span class="k">await</span> <span class="n">ArticleCrud</span><span class="o">.</span><span class="n">get_multi</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">load_options</span><span class="o">=</span><span class="p">[])</span>
|
|
</span></code></pre></div>
|
|
<h2 id="many-to-many-relationships">Many-to-many relationships<a class="headerlink" href="#many-to-many-relationships" title="Permanent link">¶</a></h2>
|
|
<p>Use <code>m2m_fields</code> to map schema fields containing lists of IDs to SQLAlchemy relationships. The CRUD class resolves and validates all IDs before persisting:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-40-1"><a id="__codelineno-40-1" name="__codelineno-40-1" href="#__codelineno-40-1"></a><span class="n">PostCrud</span> <span class="o">=</span> <span class="n">CrudFactory</span><span class="p">(</span>
|
|
</span><span id="__span-40-2"><a id="__codelineno-40-2" name="__codelineno-40-2" href="#__codelineno-40-2"></a> <span class="n">model</span><span class="o">=</span><span class="n">Post</span><span class="p">,</span>
|
|
</span><span id="__span-40-3"><a id="__codelineno-40-3" name="__codelineno-40-3" href="#__codelineno-40-3"></a> <span class="n">m2m_fields</span><span class="o">=</span><span class="p">{</span><span class="s2">"tag_ids"</span><span class="p">:</span> <span class="n">Post</span><span class="o">.</span><span class="n">tags</span><span class="p">},</span>
|
|
</span><span id="__span-40-4"><a id="__codelineno-40-4" name="__codelineno-40-4" href="#__codelineno-40-4"></a><span class="p">)</span>
|
|
</span><span id="__span-40-5"><a id="__codelineno-40-5" name="__codelineno-40-5" href="#__codelineno-40-5"></a>
|
|
</span><span id="__span-40-6"><a id="__codelineno-40-6" name="__codelineno-40-6" href="#__codelineno-40-6"></a><span class="n">post</span> <span class="o">=</span> <span class="k">await</span> <span class="n">PostCrud</span><span class="o">.</span><span class="n">create</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">obj</span><span class="o">=</span><span class="n">PostCreateSchema</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="s2">"Hello"</span><span class="p">,</span> <span class="n">tag_ids</span><span class="o">=</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]))</span>
|
|
</span></code></pre></div>
|
|
<h2 id="upsert">Upsert<a class="headerlink" href="#upsert" title="Permanent link">¶</a></h2>
|
|
<p>Atomic <code>INSERT ... ON CONFLICT DO UPDATE</code> using PostgreSQL:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-41-1"><a id="__codelineno-41-1" name="__codelineno-41-1" href="#__codelineno-41-1"></a><span class="k">await</span> <span class="n">UserCrud</span><span class="o">.</span><span class="n">upsert</span><span class="p">(</span>
|
|
</span><span id="__span-41-2"><a id="__codelineno-41-2" name="__codelineno-41-2" href="#__codelineno-41-2"></a> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span>
|
|
</span><span id="__span-41-3"><a id="__codelineno-41-3" name="__codelineno-41-3" href="#__codelineno-41-3"></a> <span class="n">obj</span><span class="o">=</span><span class="n">UserCreateSchema</span><span class="p">(</span><span class="n">email</span><span class="o">=</span><span class="s2">"alice@example.com"</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="s2">"alice"</span><span class="p">),</span>
|
|
</span><span id="__span-41-4"><a id="__codelineno-41-4" name="__codelineno-41-4" href="#__codelineno-41-4"></a> <span class="n">index_elements</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">email</span><span class="p">],</span>
|
|
</span><span id="__span-41-5"><a id="__codelineno-41-5" name="__codelineno-41-5" href="#__codelineno-41-5"></a> <span class="n">set_</span><span class="o">=</span><span class="p">{</span><span class="s2">"username"</span><span class="p">},</span>
|
|
</span><span id="__span-41-6"><a id="__codelineno-41-6" name="__codelineno-41-6" href="#__codelineno-41-6"></a><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<h2 id="response-serialization">Response serialization<a class="headerlink" href="#response-serialization" title="Permanent link">¶</a></h2>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">Added in <code>v1.1</code></p>
|
|
</div>
|
|
<p>Pass a Pydantic schema class to <code>create</code>, <code>get</code>, <code>update</code>, or <code>offset_paginate</code> to serialize the result directly into that schema and wrap it in a <a href="../../reference/schemas/#fastapi_toolsets.schemas.Response"><code>Response[schema]</code></a> or <a href="../../reference/schemas/#fastapi_toolsets.schemas.PaginatedResponse"><code>PaginatedResponse[schema]</code></a>:</p>
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-42-1"><a id="__codelineno-42-1" name="__codelineno-42-1" href="#__codelineno-42-1"></a><span class="k">class</span><span class="w"> </span><span class="nc">UserRead</span><span class="p">(</span><span class="n">PydanticBase</span><span class="p">):</span>
|
|
</span><span id="__span-42-2"><a id="__codelineno-42-2" name="__codelineno-42-2" href="#__codelineno-42-2"></a> <span class="nb">id</span><span class="p">:</span> <span class="n">UUID</span>
|
|
</span><span id="__span-42-3"><a id="__codelineno-42-3" name="__codelineno-42-3" href="#__codelineno-42-3"></a> <span class="n">username</span><span class="p">:</span> <span class="nb">str</span>
|
|
</span><span id="__span-42-4"><a id="__codelineno-42-4" name="__codelineno-42-4" href="#__codelineno-42-4"></a>
|
|
</span><span id="__span-42-5"><a id="__codelineno-42-5" name="__codelineno-42-5" href="#__codelineno-42-5"></a><span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span>
|
|
</span><span id="__span-42-6"><a id="__codelineno-42-6" name="__codelineno-42-6" href="#__codelineno-42-6"></a> <span class="s2">"/</span><span class="si">{uuid}</span><span class="s2">"</span><span class="p">,</span>
|
|
</span><span id="__span-42-7"><a id="__codelineno-42-7" name="__codelineno-42-7" href="#__codelineno-42-7"></a> <span class="n">responses</span><span class="o">=</span><span class="n">generate_error_responses</span><span class="p">(</span><span class="n">NotFoundError</span><span class="p">),</span>
|
|
</span><span id="__span-42-8"><a id="__codelineno-42-8" name="__codelineno-42-8" href="#__codelineno-42-8"></a><span class="p">)</span>
|
|
</span><span id="__span-42-9"><a id="__codelineno-42-9" name="__codelineno-42-9" href="#__codelineno-42-9"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_user</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">SessionDep</span><span class="p">,</span> <span class="n">uuid</span><span class="p">:</span> <span class="n">UUID</span><span class="p">)</span> <span class="o">-></span> <span class="n">Response</span><span class="p">[</span><span class="n">UserRead</span><span class="p">]:</span>
|
|
</span><span id="__span-42-10"><a id="__codelineno-42-10" name="__codelineno-42-10" href="#__codelineno-42-10"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">crud</span><span class="o">.</span><span class="n">UserCrud</span><span class="o">.</span><span class="n">get</span><span class="p">(</span>
|
|
</span><span id="__span-42-11"><a id="__codelineno-42-11" name="__codelineno-42-11" href="#__codelineno-42-11"></a> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span>
|
|
</span><span id="__span-42-12"><a id="__codelineno-42-12" name="__codelineno-42-12" href="#__codelineno-42-12"></a> <span class="n">filters</span><span class="o">=</span><span class="p">[</span><span class="n">User</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="n">uuid</span><span class="p">],</span>
|
|
</span><span id="__span-42-13"><a id="__codelineno-42-13" name="__codelineno-42-13" href="#__codelineno-42-13"></a> <span class="n">schema</span><span class="o">=</span><span class="n">UserRead</span><span class="p">,</span>
|
|
</span><span id="__span-42-14"><a id="__codelineno-42-14" name="__codelineno-42-14" href="#__codelineno-42-14"></a> <span class="p">)</span>
|
|
</span><span id="__span-42-15"><a id="__codelineno-42-15" name="__codelineno-42-15" href="#__codelineno-42-15"></a>
|
|
</span><span id="__span-42-16"><a id="__codelineno-42-16" name="__codelineno-42-16" href="#__codelineno-42-16"></a><span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">""</span><span class="p">)</span>
|
|
</span><span id="__span-42-17"><a id="__codelineno-42-17" name="__codelineno-42-17" href="#__codelineno-42-17"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_users</span><span class="p">(</span>
|
|
</span><span id="__span-42-18"><a id="__codelineno-42-18" name="__codelineno-42-18" href="#__codelineno-42-18"></a> <span class="n">session</span><span class="p">:</span> <span class="n">SessionDep</span><span class="p">,</span>
|
|
</span><span id="__span-42-19"><a id="__codelineno-42-19" name="__codelineno-42-19" href="#__codelineno-42-19"></a> <span class="n">params</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">dict</span><span class="p">,</span> <span class="n">Depends</span><span class="p">(</span><span class="n">crud</span><span class="o">.</span><span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate_params</span><span class="p">())],</span>
|
|
</span><span id="__span-42-20"><a id="__codelineno-42-20" name="__codelineno-42-20" href="#__codelineno-42-20"></a><span class="p">)</span> <span class="o">-></span> <span class="n">OffsetPaginatedResponse</span><span class="p">[</span><span class="n">UserRead</span><span class="p">]:</span>
|
|
</span><span id="__span-42-21"><a id="__codelineno-42-21" name="__codelineno-42-21" href="#__codelineno-42-21"></a> <span class="k">return</span> <span class="k">await</span> <span class="n">crud</span><span class="o">.</span><span class="n">UserCrud</span><span class="o">.</span><span class="n">offset_paginate</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="o">**</span><span class="n">params</span><span class="p">,</span> <span class="n">schema</span><span class="o">=</span><span class="n">UserRead</span><span class="p">)</span>
|
|
</span></code></pre></div>
|
|
<p>The schema must have <code>from_attributes=True</code> (or inherit from <a href="../../reference/schemas/#fastapi_toolsets.schemas.PydanticBase"><code>PydanticBase</code></a>) so it can be built from SQLAlchemy model instances.</p>
|
|
<hr />
|
|
<p><a href="../../reference/crud/"><span class="twemoji"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 7H5a2 2 0 0 0-2 2v8h2v-4h2v4h2V9a2 2 0 0 0-2-2m0 4H5V9h2m7-2h-4v10h2v-4h2a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2m0 4h-2V9h2m6 0v6h1v2h-4v-2h1V9h-1V7h4v2Z"/></svg></span> API Reference</a></p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</article>
|
|
</div>
|
|
|
|
|
|
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
|
|
|
|
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
|
|
</div>
|
|
|
|
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-circle-arrow-up" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="m16 12-4-4-4 4M12 16V8"/></svg>
|
|
Back to top
|
|
</button>
|
|
|
|
</main>
|
|
|
|
<footer class="md-footer">
|
|
|
|
|
|
|
|
<nav class="md-footer__inner md-grid" aria-label="Footer" >
|
|
|
|
|
|
<a href="../cli/" class="md-footer__link md-footer__link--prev" aria-label="Previous: CLI">
|
|
<div class="md-footer__button md-icon">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-arrow-left" viewBox="0 0 24 24"><path d="m12 19-7-7 7-7M19 12H5"/></svg>
|
|
</div>
|
|
<div class="md-footer__title">
|
|
<span class="md-footer__direction">
|
|
Previous
|
|
</span>
|
|
<div class="md-ellipsis">
|
|
CLI
|
|
</div>
|
|
</div>
|
|
</a>
|
|
|
|
|
|
|
|
<a href="../db/" class="md-footer__link md-footer__link--next" aria-label="Next: Database">
|
|
<div class="md-footer__title">
|
|
<span class="md-footer__direction">
|
|
Next
|
|
</span>
|
|
<div class="md-ellipsis">
|
|
Database
|
|
</div>
|
|
</div>
|
|
<div class="md-footer__button md-icon">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-arrow-right" viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
|
|
</div>
|
|
</a>
|
|
|
|
</nav>
|
|
|
|
|
|
<div class="md-footer-meta md-typeset">
|
|
<div class="md-footer-meta__inner md-grid">
|
|
<div class="md-copyright">
|
|
|
|
<div class="md-copyright__highlight">
|
|
Copyright © 2026 d3vyce
|
|
</div>
|
|
|
|
|
|
Made with
|
|
<a href="https://zensical.org/" target="_blank" rel="noopener">
|
|
Zensical
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
</div>
|
|
<div class="md-dialog" data-md-component="dialog">
|
|
<div class="md-dialog__inner md-typeset"></div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<script id="__config" type="application/json">{"annotate":null,"base":"../..","features":["announce.dismiss","content.action.view","content.code.annotate","content.code.copy","content.code.select","content.footnote.tooltips","content.tabs.link","content.tooltips","navigation.footer","navigation.indexes","navigation.instant","navigation.instant.prefetch","navigation.path","navigation.sections","navigation.tabs","navigation.top","navigation.tracking","search.highlight"],"search":"../../assets/javascripts/workers/search.e2d2d235.min.js","tags":null,"translations":{"clipboard.copied":"Copied to clipboard","clipboard.copy":"Copy to clipboard","search.result.more.one":"1 more on this page","search.result.more.other":"# more on this page","search.result.none":"No matching documents","search.result.one":"1 matching document","search.result.other":"# matching documents","search.result.placeholder":"Type to start searching","search.result.term.missing":"Missing","select.version":"Select version"},"version":{"alias":true,"default":"stable","provider":"mike"}}</script>
|
|
|
|
|
|
<script src="../../assets/javascripts/bundle.91a19a9e.min.js"></script>
|
|
|
|
|
|
</body>
|
|
</html> |