pub struct DynQuerySet<'a> { /* private fields */ }Expand description
A runtime-typed, lazy SQL query against one ModelMeta.
Built by DynQuerySet::for_meta; chain .search(...) /
.filter_eq_string(...) / .order_by_col(...) / .limit(...) /
.offset(...) to refine; finish with .count() or
.fetch_as_strings().
Implementations§
Source§impl<'a> DynQuerySet<'a>
impl<'a> DynQuerySet<'a>
Sourcepub fn for_meta(meta: &'a ModelMeta) -> Self
pub fn for_meta(meta: &'a ModelMeta) -> Self
Start a SELECT against the model’s table. The column list
defaults to every field in declaration order; restrict it with
.select_cols(...) before fetching when you only want a subset.
Sourcepub fn with_deleted(self) -> Self
pub fn with_deleted(self) -> Self
Include soft-deleted rows for models tagged with
#[umbral(soft_delete)].
Sourcepub fn only_deleted(self) -> Self
pub fn only_deleted(self) -> Self
Restrict a soft-delete model to only rows whose deleted_at is
populated.
Sourcepub fn hard_delete(self) -> Self
pub fn hard_delete(self) -> Self
Force a real DELETE for a soft-delete model.
Sourcepub fn select_cols(self, cols: &[String]) -> Self
pub fn select_cols(self, cols: &[String]) -> Self
Restrict the SELECT list to the supplied column names. Names
that don’t exist on the model are silently dropped so a stale
list_display config can’t crash a request.
Expand the named FK columns via a batched IN (...) lookup
after the main query — mirrors the typed
QuerySet::select_related shape (single-hop and __-chained
alike). After this call, every FK field along the chain in
the response JSON renders as the full related-row object
instead of the raw integer id. Query budget is
1 + len(hops) per chain regardless of how many parent rows
came back (no N+1) — gap2 #18.
Names may use either . (URL-natural) or __ (lookup
style) as the hop separator; both normalize to the
same canonical chain internally. Mixed separators in one
token (e.g. author.profile__org) are accepted too.
Names that don’t exist on the model OR aren’t FK columns at
any hop are silently dropped — the REST plugin’s ?include=
handler does its own up-front validation with a 400 on
unknown names, so stale dynamic includes (e.g. an internal
call site that hardcoded a name that was later renamed) just
skip without crashing the request.
DynQuerySet::for_meta(&meta)
.select_related_dyn(&["user".into(), "author.profile".into()])
.fetch_as_json().awaitSourcepub fn search(self, fields: &[String], term: &str) -> Self
pub fn search(self, fields: &[String], term: &str) -> Self
Add WHERE (<predicate1> OR <predicate2> OR ...) for a free-text
term against the model’s searchable columns. Per-column predicate
depends on the column’s SqlType:
| SqlType | Predicate |
|---|---|
Text | UPPER(col) LIKE '%TERM%' — case-insensitive substring |
SmallInt / Integer / BigInt / ForeignKey | col = term when term.parse::<i64>().is_ok() |
Real / Double | col = term when term.parse::<f64>().is_ok() |
Boolean | col = term when term parses as true / false |
| everything else (Date, Time, Uuid, Json, Bytes, Array, …) | skipped |
fields controls which columns participate:
- Non-empty: restricted to the named columns. Names that don’t exist on the model are silently dropped.
- Empty: every column on the model is a candidate; the
per-type table above decides which actually contribute. This
is the “no
search_fieldsconfigured” default behaviour.
Empty term (after trimming) is always a no-op. If the column
selection results in zero predicates (e.g. term = "abc" and
the only candidate columns are numeric), nothing is appended.
Sourcepub fn filter_condition(self, cond: Condition) -> Self
pub fn filter_condition(self, cond: Condition) -> Self
Splice an externally-built sea_query::Condition into the
accumulated WHERE clauses. Used by callers that need lookups
the typed builder methods don’t cover (e.g. umbral-rest’s
query-string filter parser produces a Condition per
field__lookup=value triple and feeds it in here).
Sourcepub fn filter_in_i64(self, col: &str, vals: &[i64]) -> Self
pub fn filter_in_i64(self, col: &str, vals: &[i64]) -> Self
Add WHERE <col> IN (?, ?, ...) for an i64 column (PK / FK).
Empty vals is a no-op; unknown columns are silently dropped.
Sourcepub fn filter_m2m_contains_any(
self,
field_name: &str,
child_ids: &[String],
) -> Self
pub fn filter_m2m_contains_any( self, field_name: &str, child_ids: &[String], ) -> Self
Filter the parent set down to rows that have an M2M link to at
least one of child_ids through the named M2M field. Emits:
WHERE <pk> IN (
SELECT parent_id FROM <parent_table>_<field_name>
WHERE child_id IN (?, ?, ...)
)The junction table name follows the framework’s
{parent_table}_{field_name} convention (same as
set_junction_dynamic and the migration emitter use). Returns
self unchanged when:
child_idsis empty,- no M2M relation with that
field_nameexists on the model, - the parent model has no PK column,
- every value in
child_idsfails to parse asi64(M2M PKs are i64 at v1 across the framework).
Use case: admin filter for “products with tag 1 OR tag 2 OR tag 3” — call once with all three child ids; the IN subquery is one round-trip regardless of selection count.
Sourcepub fn filter_in_strings(self, col: &str, vals: &[String]) -> Self
pub fn filter_in_strings(self, col: &str, vals: &[String]) -> Self
Add WHERE <col> IN (?, ?, ...) for any column. Each value is
parsed against the column’s SqlType (same coercion as
Self::filter_eq_string) so SQLite’s affinity rules see the
right operand type. Values that fail to parse are dropped from
the IN list. Empty vals (or all-unparseable) is a no-op;
unknown columns are silently dropped.
Single-value calls degenerate to <col> = ? via sea-query’s
is_in lowering — callers can use this for both the “one
selection” and “multi-selection” filter paths and get the
natural SQL in each case.
Sourcepub fn filter_eq_string(self, col: &str, value: &str) -> Self
pub fn filter_eq_string(self, col: &str, value: &str) -> Self
Add WHERE <col> = <value> where the value is parsed against
the column’s SqlType so SQLite’s affinity rules see the right
operand type.
Sourcepub fn order_by_col(self, col: &str, descending: bool) -> Self
pub fn order_by_col(self, col: &str, descending: bool) -> Self
Add ORDER BY <col> ASC|DESC. Unknown columns are silently
dropped. Multiple calls append (sea-query semantics).
Sourcepub async fn count(self) -> Result<i64, DynError>
pub async fn count(self) -> Result<i64, DynError>
Terminal: SELECT COUNT(*) with the accumulated WHERE
clauses. ORDER BY / LIMIT / OFFSET are dropped (irrelevant
to a count).
Sourcepub async fn fetch_distinct_strings(
self,
col: &str,
) -> Result<Vec<String>, DynError>
pub async fn fetch_distinct_strings( self, col: &str, ) -> Result<Vec<String>, DynError>
Terminal: SELECT DISTINCT <col> with the accumulated WHERE.
Returns each value as a string (via decode_to_string). LIMIT
is honoured; ORDER BY isn’t (DISTINCT ordering is whatever the
underlying scan yields). Unknown column → empty result.
Sourcepub async fn delete(self) -> Result<u64, DynError>
pub async fn delete(self) -> Result<u64, DynError>
Terminal: DELETE FROM <table> with the accumulated WHERE.
Returns the number of rows affected.
gaps #77: pre-collects the affected PKs (one extra SELECT per
call) before the DELETE so bulk_post_delete:<table> can fire
with the actual row ids. Subscribers that need to invalidate
caches / write audit-log rows / sync a search index get the
list of PKs that just left the table, not just a row count.
Sourcepub async fn restore(self) -> Result<u64, DynError>
pub async fn restore(self) -> Result<u64, DynError>
Terminal: undo a soft-delete — UPDATE <table> SET deleted_at = NULL for the rows matching the accumulated WHERE that are
currently soft-deleted (deleted_at IS NOT NULL). Returns the
number of rows restored. A no-op (0 rows) on a model that isn’t
tagged soft_delete, since there is no deleted_at column to
clear — the caller should gate on meta.soft_delete first.
This is the inverse of Self::delete on a soft-delete model:
delete() stamps deleted_at = now(), restore() clears it.
The admin’s “Restore selected” trash action drives this.
Sourcepub async fn update_one(self, col: &str, value: &str) -> Result<u64, DynError>
pub async fn update_one(self, col: &str, value: &str) -> Result<u64, DynError>
Terminal: UPDATE <table> SET <col> = <value> with the
accumulated WHERE. The value is parsed against the column’s
SqlType so SQLite affinity sees the right operand. Returns
the number of rows affected. Unknown column → 0 rows.
Sourcepub async fn update_form(
self,
form: &HashMap<String, String>,
skip: &[String],
) -> Result<u64, DynError>
pub async fn update_form( self, form: &HashMap<String, String>, skip: &[String], ) -> Result<u64, DynError>
Terminal: UPDATE <table> SET <col1> = ?, <col2> = ?, ... with
the accumulated WHERE. Each form value is parsed against its
column’s SqlType. The primary key column is silently dropped
from the form (it’s the filter, not a target). skip lists
columns the caller wants excluded (e.g. readonly fields the
admin already enforced). Returns rows affected.
Sourcepub async fn update_form_in_tx(
self,
tx: &mut Transaction,
form: &HashMap<String, String>,
skip: &[String],
) -> Result<u64, DynError>
pub async fn update_form_in_tx( self, tx: &mut Transaction, form: &HashMap<String, String>, skip: &[String], ) -> Result<u64, DynError>
Transaction-aware sibling of Self::update_form. Builds and
executes the identical UPDATE (same per-field validation,
skip / PK exclusion, auto_now refresh, WriteError::Validator
shape, and accumulated WHERE) but runs it on the caller-supplied
tx. The caller owns commit / rollback, so the update is
uncommitted until they say so — used by the admin to save a
parent edit and its inline child changes atomically.
Sourcepub async fn insert_form(
self,
form: &HashMap<String, String>,
skip: &[String],
) -> Result<i64, DynError>
pub async fn insert_form( self, form: &HashMap<String, String>, skip: &[String], ) -> Result<i64, DynError>
Terminal: INSERT INTO <table> (...) VALUES (...) from a form
map. Auto-increment integer PKs are omitted when the form value
is missing or empty (SQLite hands out the next id). Form keys
that don’t match a column are ignored. skip lets the caller
drop fields the admin pre-filtered. Returns last_insert_rowid.
Sourcepub async fn insert_form_in_tx(
self,
tx: &mut Transaction,
form: &HashMap<String, String>,
skip: &[String],
) -> Result<i64, DynError>
pub async fn insert_form_in_tx( self, tx: &mut Transaction, form: &HashMap<String, String>, skip: &[String], ) -> Result<i64, DynError>
Transaction-aware sibling of Self::insert_form. Builds and
executes the identical INSERT (same form_str_to_sea_value
per-field validation, same skip / auto-increment-PK / auto-now
handling, same WriteError::Validator shape, same returned-PK
semantics) but runs it on the caller-supplied tx instead of a
fresh pool connection. The caller owns commit / rollback, so
the insert is uncommitted until they say so — this is what lets
the admin save a parent row and its inline children atomically.
Sourcepub async fn fetch_as_strings(
self,
) -> Result<Vec<HashMap<String, String>>, DynError>
pub async fn fetch_as_strings( self, ) -> Result<Vec<HashMap<String, String>>, DynError>
Terminal: fetch every row, decoding each cell to its string
form via decode_to_string. Returns one HashMap per row,
keyed by column name, holding only the columns named in
select_cols (defaults to all).
Sourcepub async fn fetch_as_json(self) -> Result<Vec<Map<String, Value>>, DynError>
pub async fn fetch_as_json(self) -> Result<Vec<Map<String, Value>>, DynError>
Terminal: fetch every row, decoding each cell to a
serde_json::Value that preserves JSON shape (numbers stay
numbers, booleans stay booleans, JSON columns nest verbatim).
The right shape for HTTP API responses. Returns one
serde_json::Map per row, keyed by column name.
Sourcepub async fn first_as_json(self) -> Result<Option<Map<String, Value>>, DynError>
pub async fn first_as_json(self) -> Result<Option<Map<String, Value>>, DynError>
Terminal: fetch the first row (LIMIT 1) as a JSON object.
Returns None when the filter matches zero rows.
Sourcepub async fn fetch_one_json_in_tx(
self,
tx: &mut Transaction,
) -> Result<Option<Map<String, Value>>, DynError>
pub async fn fetch_one_json_in_tx( self, tx: &mut Transaction, ) -> Result<Option<Map<String, Value>>, DynError>
Transaction-aware single-row read: SELECT <cols> ... LIMIT 1 for
the accumulated WHERE, run on the open tx. Decodes every model
column into a JSON map. Used by REST bulk update to read a row back
on the same (uncommitted) transaction so the response reflects the
in-flight write. Returns None when the filter matches no row.
Unlike Self::fetch_as_json this does NOT hydrate M2M arrays or
select_related — it’s the column-level read the bulk write path
needs, matching what the single-object PATCH read-back returns.
Sourcepub async fn insert_json(
self,
body: &Map<String, Value>,
) -> Result<Map<String, Value>, WriteError>
pub async fn insert_json( self, body: &Map<String, Value>, ) -> Result<Map<String, Value>, WriteError>
Terminal: INSERT one row from a JSON map. Auto-increment integer
PKs are omitted when missing or null (the backend assigns).
Returns the newly-inserted row as JSON (via RETURNING * on
Postgres; via last_insert_rowid → SELECT * on SQLite). The
per-column JSON-to-SeaValue coercion goes through the existing
json_to_sea_value so timestamp / uuid / json paths are the
same as the typed Manager::create path.
Sourcepub async fn insert_json_in_tx(
self,
body: &Map<String, Value>,
tx: &mut Transaction,
) -> Result<Map<String, Value>, WriteError>
pub async fn insert_json_in_tx( self, body: &Map<String, Value>, tx: &mut Transaction, ) -> Result<Map<String, Value>, WriteError>
Terminal: INSERT one row from a JSON map ON the passed
transaction. The transactional sibling of Self::insert_json:
the INSERT, the PK re-fetch, the M2M junction writes, the M2M
read-back, AND the FK-existence validation all execute on tx
rather than the ambient pool — so a caller can insert a parent
and its children on one transaction and have the whole set
commit (or roll back) atomically (planning/orm_fixes.md #2).
Validation runs against the open transaction
(crate::orm::validation::validate_on_create_in_tx) so a
child whose FK targets a parent inserted earlier on the same
(uncommitted) tx resolves. This is what makes a true-atomic
nested create possible without the old compensating-delete
dance.
Signals. Unlike the auto-commit path, this does NOT fire
pre_save / post_save. The row isn’t durable until the
caller commits tx, and a subscriber (audit log, cache
invalidation, search index) firing before commit could observe
— or react to — a write that then rolls back. The caller owns
the commit, so the caller owns whatever post-commit signalling
it wants. (The typed Manager::create_in_tx path makes the
same choice for the same reason.)
Sourcepub async fn update_json_in_tx(
self,
body: &Map<String, Value>,
tx: &mut Transaction,
) -> Result<u64, WriteError>
pub async fn update_json_in_tx( self, body: &Map<String, Value>, tx: &mut Transaction, ) -> Result<u64, WriteError>
Transaction-aware sibling of Self::update_json. PATCH semantics —
update only the columns present in body for the rows matched by the
accumulated WHERE — but every statement runs on the open tx so a
batch of updates commits or rolls back as a unit. M2M arrays in the
body are mirrored into junction tables on the same tx. Returns the
number of rows touched.
Used by REST bulk update (one tx for the whole array). Mirrors the
pool path’s validation + noform/slug_from/auto_now handling;
the only difference is the execution target.
Sourcepub async fn delete_in_tx(self, tx: &mut Transaction) -> Result<u64, DynError>
pub async fn delete_in_tx(self, tx: &mut Transaction) -> Result<u64, DynError>
Transaction-aware sibling of Self::delete. Deletes (or
soft-deletes, for a soft_delete model) the rows matched by the
accumulated WHERE on the open tx, so a batch of deletes commits or
rolls back as a unit. Returns the number of rows affected.
Soft-delete models stamp deleted_at = now() (consistent with the
pool path / gaps #35) unless Self::hard_delete was set.
Sourcepub async fn update_json(
self,
body: &Map<String, Value>,
) -> Result<u64, WriteError>
pub async fn update_json( self, body: &Map<String, Value>, ) -> Result<u64, WriteError>
Terminal: PATCH semantics — update only the columns present
in body. The accumulated WHERE clauses narrow the target
row(s). Returns the number of rows affected.
Auto Trait Implementations§
impl<'a> !RefUnwindSafe for DynQuerySet<'a>
impl<'a> !UnwindSafe for DynQuerySet<'a>
impl<'a> Freeze for DynQuerySet<'a>
impl<'a> Send for DynQuerySet<'a>
impl<'a> Sync for DynQuerySet<'a>
impl<'a> Unpin for DynQuerySet<'a>
impl<'a> UnsafeUnpin for DynQuerySet<'a>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi Quirk value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);