Convenience namespace exposing the most common parts of the library's public API for day-to-day usage (i.e., not implementing anything advanced)
Convenience namespace exposing the most common parts of the library's public API for day-to-day usage (i.e., not implementing anything advanced)
(batched-hydrate model k rows)(changes instance)Get a map with any changes made to instance since it came out of the DB. Only includes keys that have been
added or given different values; keys that were removed are not counted. Returns nil if there are no changes.
Get a map with any changes made to `instance` since it came out of the DB. Only includes keys that have been added or given different values; keys that were removed are not counted. Returns `nil` if there are no changes.
(compile & body)Return the compiled query that would be executed by a form, rather than executing that form itself.
(delete/delete :table :id 1) => ["DELETE FROM table WHERE ID = ?" 1]
Return the compiled query that would be executed by a form, rather than executing that form itself. (delete/delete :table :id 1) => ["DELETE FROM table WHERE ID = ?" 1]
(count modelable & kv-args? query?)(current instance)Return the underlying map representing the current state of an instance.
Return the underlying map representing the current state of an `instance`.
(default-connectable model)The default connectable that should be used when executing queries for model if
no toucan2.connection/*current-connectable* is currently bound. By default, this just returns the global default
connectable, :default, but you can tell Toucan to use a different default connectable for a model by implementing
this method.
The default connectable that should be used when executing queries for `model` if no [[toucan2.connection/*current-connectable*]] is currently bound. By default, this just returns the global default connectable, `:default`, but you can tell Toucan to use a different default connectable for a model by implementing this method.
(define-after-insert model [instance-binding] & body)(define-after-select model [instance-binding] & body)(define-after-update model [instance-binding] & body)(define-before-delete model [instance-binding] & body)(define-before-insert model [instance-binding] & body)(define-before-select model [args-binding] & body)(define-before-select [model query-class] [args-binding] & body)(define-before-update model [row-binding] & body)(define-default-fields model & body)(deftransforms model transforms)Define type transforms to use for a specific model. transforms should be a map of
{:column-name {:in <fn>, :out <fn>}}
:in transforms are applied to values going over the wire to the database; these generally only applied to values
passed at or near the top level to various functions; don't expect Toucan 2 to parse your SQL to find out which
parameter corresponds to what in order to apply transforms or to apply transforms inside JOINS in hand-written
HoneySQL. That said, unless you're doing something weird your transforms should generally get applied.
:out transforms are applied to values coming out of the database; since nothing weird really happens there this is
done consistently.
Transform functions for either case are skipped for nil values.
Example:
(deftransforms :models/user {:type {:in name, :out keyword}})
You can also define transforms independently, and derive a model from them:
(deftransforms ::type-keyword {:type {:in name, :out keyword}})
(derive :models/user ::type-keyword) (derive :models/user ::some-other-transform)
Don't derive a model from multiple deftransforms for the same key in the same direction.
When multiple transforms match a given model they are combined into a single map of transforms with merge-with merge. If multiple transforms match a given column in a given direction, only one of them will be used; you should
assume which one is used is indeterminate. (This may be made an error, or at least a warning, in the future.)
Until upstream issue https://github.com/camsaul/methodical/issues/97 is resolved, you will have to specify which method should be applied first in cases of ambiguity using [[methodical.core/prefer-method!]]:
(m/prefer-method! transformed/transforms ::user-with-location ::user-with-password)
If you want to override transforms completely for a model, and ignore transforms from ancestors of a model, you can
create an :around method:
(defmethod toucan2.tools.transformed/transforms :around ::my-model [_model] {:field {:in name, :out keyword}})
Define type transforms to use for a specific model. `transforms` should be a map of
{:column-name {:in <fn>, :out <fn>}}
`:in` transforms are applied to values going over the wire to the database; these generally only applied to values
passed at or near the top level to various functions; don't expect Toucan 2 to parse your SQL to find out which
parameter corresponds to what in order to apply transforms or to apply transforms inside JOINS in hand-written
HoneySQL. That said, unless you're doing something weird your transforms should generally get applied.
`:out` transforms are applied to values coming out of the database; since nothing weird really happens there this is
done consistently.
Transform functions for either case are skipped for `nil` values.
Example:
(deftransforms :models/user
{:type {:in name, :out keyword}})
You can also define transforms independently, and derive a model from them:
(deftransforms ::type-keyword
{:type {:in name, :out keyword}})
(derive :models/user ::type-keyword)
(derive :models/user ::some-other-transform)
Don't derive a model from multiple `deftransforms` for the same key in the same direction.
When multiple transforms match a given model they are combined into a single map of transforms with `merge-with
merge`. If multiple transforms match a given column in a given direction, only one of them will be used; you should
assume which one is used is indeterminate. (This may be made an error, or at least a warning, in the future.)
Until upstream issue https://github.com/camsaul/methodical/issues/97 is resolved, you will have to specify which
method should be applied first in cases of ambiguity using [[methodical.core/prefer-method!]]:
(m/prefer-method! transformed/transforms ::user-with-location ::user-with-password)
If you want to override transforms completely for a model, and ignore transforms from ancestors of a model, you can
create an `:around` method:
(defmethod toucan2.tools.transformed/transforms :around ::my-model
[_model]
{:field {:in name, :out keyword}})(delete! modelable & conditions? query?)Returns number of rows deleted.
Returns number of rows deleted.
(do-with-connection connectable f)(exists? modelable & kv-args? query?)(hydrate results k & ks)Hydrate a single object or sequence of objects.
[[toucan2.hydrate/hydrate]] attempts to do a batched hydration where possible. If the key being hydrated is
defined as one of some table's [[toucan2.hydrate/model-for-automagic-hydration]], hydrate will do a batched
toucan2.select/select if a corresponding key (by default, the same key suffixed by -id) is found in the
objects being batch hydrated. The corresponding key can be customized by
implementing [[toucan2.hydrate/fk-keys-for-automagic-hydration]].
(hydrate [{:user_id 100}, {:user_id 101}] :user)
Since :user is a hydration key for :models/User, a single toucan2.select/select will used to fetch Users:
(db/select :models/User :id [:in #{100 101}])
The corresponding Users are then added under the key :user.
If the key can't be hydrated auto-magically with the appropriate [[toucan2.hydrate/model-for-automagic-hydration]], [[toucan2.hydrate/hydrate]] will attempt to do batched hydration if it can find a matching method for [[toucan2.hydrate/batched-hydrate]]. If a matching function is found, it is called with a collection of objects, e.g.
(m/defmethod hydrate/batched-hydrate [:default :fields] [_model _k rows] (let [id->fields (get-some-fields rows)] (for [row rows] (assoc row :fields (get id->fields (:id row))))))
If the key is not eligible for batched hydration, [[toucan2.hydrate/hydrate]] will look for a matching
[[toucan2.hydrate/simple-hydrate]] method. simple-hydrate is called with a single row.
(m/defmethod simple-hydrate [:default :dashboard] [_model _k {:keys [dashboard-id], :as row}] (assoc row :dashboard (select/select-one :models/Dashboard :toucan/pk dashboard-id)))
You can hydrate several keys at one time:
(hydrate {...} :a :b) -> {:a 1, :b 2}
You can do recursive hydration by listing keys inside a vector:
(hydrate {...} [:a :b]) -> {:a {:b 1}}
The first key in a vector will be hydrated normally, and any subsequent keys will be hydrated inside the corresponding values for that key.
(hydrate {...} [:a [:b :c] :e]) -> {:a {:b {:c 1} :e 2}}
Hydrate a single object or sequence of objects.
#### Automagic Batched Hydration (via [[toucan2.hydrate/model-for-automagic-hydration]])
[[toucan2.hydrate/hydrate]] attempts to do a *batched hydration* where possible. If the key being hydrated is
defined as one of some table's [[toucan2.hydrate/model-for-automagic-hydration]], `hydrate` will do a batched
[[toucan2.select/select]] if a corresponding key (by default, the same key suffixed by `-id`) is found in the
objects being batch hydrated. The corresponding key can be customized by
implementing [[toucan2.hydrate/fk-keys-for-automagic-hydration]].
(hydrate [{:user_id 100}, {:user_id 101}] :user)
Since `:user` is a hydration key for `:models/User`, a single [[toucan2.select/select]] will used to fetch Users:
(db/select :models/User :id [:in #{100 101}])
The corresponding Users are then added under the key `:user`.
#### Function-Based Batched Hydration (via [[toucan2.hydrate/batched-hydrate]] methods)
If the key can't be hydrated auto-magically with the appropriate [[toucan2.hydrate/model-for-automagic-hydration]],
[[toucan2.hydrate/hydrate]] will attempt to do batched hydration if it can find a matching method
for [[toucan2.hydrate/batched-hydrate]]. If a matching function is found, it is called with a collection of
objects, e.g.
(m/defmethod hydrate/batched-hydrate [:default :fields]
[_model _k rows]
(let [id->fields (get-some-fields rows)]
(for [row rows]
(assoc row :fields (get id->fields (:id row))))))
#### Simple Hydration (via [[toucan2.hydrate/simple-hydrate]] methods)
If the key is *not* eligible for batched hydration, [[toucan2.hydrate/hydrate]] will look for a matching
[[toucan2.hydrate/simple-hydrate]] method. `simple-hydrate` is called with a single row.
(m/defmethod simple-hydrate [:default :dashboard]
[_model _k {:keys [dashboard-id], :as row}]
(assoc row :dashboard (select/select-one :models/Dashboard :toucan/pk dashboard-id)))
#### Hydrating Multiple Keys
You can hydrate several keys at one time:
(hydrate {...} :a :b)
-> {:a 1, :b 2}
#### Nested Hydration
You can do recursive hydration by listing keys inside a vector:
(hydrate {...} [:a :b])
-> {:a {:b 1}}
The first key in a vector will be hydrated normally, and any subsequent keys will be hydrated *inside* the
corresponding values for that key.
(hydrate {...}
[:a [:b :c] :e])
-> {:a {:b {:c 1} :e 2}}(insert! modelable row-or-rows)(insert! modelable columns row-vectors)(insert! modelable k v & more)Returns number of rows inserted.
Returns number of rows inserted.
(insert-returning-instances! modelable row-or-rows)(insert-returning-instances! [modelable & columns-to-return] row-or-rows)(insert-returning-instances! modelable columns row-vectors)(insert-returning-instances! [modelable & columns-to-return]
columns
row-vectors)(insert-returning-instances! modelable k v & more)(insert-returning-instances! [modelable & columns-to-return] k v & more)Like insert!, but returns a vector of the primary keys of the newly inserted rows rather than the number of rows
inserted. The primary keys are determined by model/primary-keys. For models with a single primary key, this
returns a vector of single values, e.g. [1 2] if the primary key is :id and you've inserted rows 1 and 2; for
composite primary keys this returns a vector of tuples where each tuple has the value of corresponding primary key as
returned by model/primary-keys, e.g. for composite PK [:id :name] you might get [[1 "Cam"] [2 "Sam"]].
Like [[insert!]], but returns a vector of the primary keys of the newly inserted rows rather than the number of rows inserted. The primary keys are determined by [[model/primary-keys]]. For models with a single primary key, this returns a vector of single values, e.g. `[1 2]` if the primary key is `:id` and you've inserted rows 1 and 2; for composite primary keys this returns a vector of tuples where each tuple has the value of corresponding primary key as returned by [[model/primary-keys]], e.g. for composite PK `[:id :name]` you might get `[[1 "Cam"] [2 "Sam"]]`.
(insert-returning-pks! modelable row-or-rows)(insert-returning-pks! modelable columns row-vectors)(insert-returning-pks! modelable k v & more)Like insert!, but returns a vector of the primary keys of the newly inserted rows rather than the number of rows
inserted. The primary keys are determined by model/primary-keys. For models with a single primary key, this
returns a vector of single values, e.g. [1 2] if the primary key is :id and you've inserted rows 1 and 2; for
composite primary keys this returns a vector of tuples where each tuple has the value of corresponding primary key as
returned by model/primary-keys, e.g. for composite PK [:id :name] you might get [[1 "Cam"] [2 "Sam"]].
Like [[insert!]], but returns a vector of the primary keys of the newly inserted rows rather than the number of rows inserted. The primary keys are determined by [[model/primary-keys]]. For models with a single primary key, this returns a vector of single values, e.g. `[1 2]` if the primary key is `:id` and you've inserted rows 1 and 2; for composite primary keys this returns a vector of tuples where each tuple has the value of corresponding primary key as returned by [[model/primary-keys]], e.g. for composite PK `[:id :name]` you might get `[[1 "Cam"] [2 "Sam"]]`.
(instance model)(instance model m)(instance model k v & more)(instance-of? model x)True if x is a Toucan2 instance, and its protocols/model isa? model.
(instance-of? ::bird (instance ::toucan {})) ; -> true (instance-of? ::toucan (instance ::bird {})) ; -> false
True if `x` is a Toucan2 instance, and its [[protocols/model]] `isa?` `model`.
(instance-of? ::bird (instance ::toucan {})) ; -> true
(instance-of? ::toucan (instance ::bird {})) ; -> false(instance? x)True if x is a Toucan2 instance, i.e. a toucan2.instance.Instance or some other class that satisfies the correct
interfaces.
Toucan instances need to implement [[IModel]], [[IWithModel]], and [[IRecordChanges]].
True if `x` is a Toucan2 instance, i.e. a `toucan2.instance.Instance` or some other class that satisfies the correct interfaces. Toucan instances need to implement [[IModel]], [[IWithModel]], and [[IRecordChanges]].
(model this)Get the Toucan model associated with this.
Get the Toucan model associated with `this`.
(model-for-automagic-hydration original-model k)The model that should be used to automagically hydrate the key k in instances of original-model.
(model-for-automagic-hydration :some-table :user) :-> :myapp.models/user
The model that should be used to automagically hydrate the key `k` in instances of `original-model`. (model-for-automagic-hydration :some-table :user) :-> :myapp.models/user
(original instance)Get the original version of instance as it appeared when it first came out of the DB.
Get the original version of `instance` as it appeared when it first came out of the DB.
(primary-key-values instance)(primary-key-values model m)Return a map of primary key values for a Toucan 2 instance.
Return a map of primary key values for a Toucan 2 `instance`.
(primary-keys model)Return a sequence of the primary key columns names, as keywords, for a model.
Return a sequence of the primary key columns names, as keywords, for a model.
(primary-keys-vec model)Get the primary keys for a model as a vector.
Get the primary keys for a `model` as a vector.
(query queryable)(query connectable queryable)(query connectable modelable queryable)(query-one queryable)(query-one connectable queryable)(query-one connectable modelable queryable)(reducible-insert modelable row-or-rows)(reducible-insert modelable columns row-vectors)(reducible-insert modelable k v & more)(reducible-insert-returning-instances modelable row-or-rows)(reducible-insert-returning-instances [modelable & columns-to-return]
row-or-rows)(reducible-insert-returning-instances modelable columns row-vectors)(reducible-insert-returning-instances [modelable & columns-to-return]
columns
row-vectors)(reducible-insert-returning-instances modelable k v & more)(reducible-insert-returning-instances [modelable & columns-to-return]
k
v
&
more)(reducible-insert-returning-pks modelable row-or-rows)(reducible-insert-returning-pks modelable columns row-vectors)(reducible-insert-returning-pks modelable k v & more)(reducible-query queryable)(reducible-query connectable queryable)(reducible-query connectable modelable queryable)(reducible-select modelable & kv-args? query?)(reducible-select [modelable & columns] & kv-args? query?)(reducible-update modelable
pk?
conditions-map-or-query?
&
conditions-kv-args
changes-map)(reducible-update-returning-pks modelable
pk?
conditions-map-or-query?
&
conditions-kv-args
changes-map)(save! object)(select modelable & kv-args? query?)(select [modelable & columns] & kv-args? query?)(select-fn->fn f1 f2 modelable & kv-args? query?)(select-fn->pk f modelable & kv-args? query?)(select-fn-reducible f modelable & kv-args? query?)(select-fn-set f modelable & kv-args? query?)Like select, but returns a set of values of (f instance) for the results. Returns nil if the set is empty.
Like `select`, but returns a set of values of `(f instance)` for the results. Returns `nil` if the set is empty.
(select-fn-vec f modelable & kv-args? query?)Like select, but returns a vector of values of (f instance) for the results. Returns nil if the vector is
empty.
Like `select`, but returns a vector of values of `(f instance)` for the results. Returns `nil` if the vector is empty.
(select-one modelable & kv-args? query?)(select-one [modelable & columns] & kv-args? query?)(select-one-fn f modelable & kv-args? query?)(select-one-pk modelable & kv-args? query?)(select-pk->fn f modelable & kv-args? query?)(select-pks-fn modelable)Return a function to get the value(s) of the primary key(s) from a row. Used by toucan2.select/select-pks-reducible
and thus by toucan2.select/select-pks-set, toucan2.select/select-pks-vec, etc.
The primary keys are determined by toucan2.model/primary-keys. By default this is simply the keyword :id.
Return a function to get the value(s) of the primary key(s) from a row. Used by [[toucan2.select/select-pks-reducible]] and thus by [[toucan2.select/select-pks-set]], [[toucan2.select/select-pks-vec]], etc. The primary keys are determined by [[toucan2.model/primary-keys]]. By default this is simply the keyword `:id`.
(select-pks-set modelable & kv-args? query?)(select-pks-vec modelable & kv-args? query?)(simple-hydrate model k row)Implementations should return a version of map row with the key k added.
Implementations should return a version of map `row` with the key `k` added.
(table-name model)(transforms model)Return a map of
{column-name {:in <fn>, :out <fn>}}
For a given model, all matching transforms are combined with merge-with merge in an indeterminate order, so don't
try to specify multiple transforms for the same column in the same direction for a given model -- compose your
transform functions instead if you want to do that. See toucan2.tools.helpers/deftransforms for more info.
Return a map of
{column-name {:in <fn>, :out <fn>}}
For a given `model`, all matching transforms are combined with `merge-with merge` in an indeterminate order, so don't
try to specify multiple transforms for the same column in the same direction for a given model -- compose your
transform functions instead if you want to do that. See [[toucan2.tools.helpers/deftransforms]] for more info.(update! modelable
pk?
conditions-map-or-query?
&
conditions-kv-args
changes-map)(update-returning-pks! modelable
pk?
conditions-map-or-query?
&
conditions-kv-args
changes-map)(with-call-count [call-count-fn-binding] & body)Execute body, trackingthe number of database queries and statements executed. This number can be fetched at any
time withing body by calling function bound to call-count-fn-binding:
(with-call-count [call-count] (select ...) (println "CALLS:" (call-count)) (insert! ...) (println "CALLS:" (call-count))) ;; -> CALLS: 1 ;; -> CALLS: 2
Execute `body`, trackingthe number of database queries and statements executed. This number can be fetched at any
time withing `body` by calling function bound to `call-count-fn-binding`:
(with-call-count [call-count]
(select ...)
(println "CALLS:" (call-count))
(insert! ...)
(println "CALLS:" (call-count)))
;; -> CALLS: 1
;; -> CALLS: 2(with-compiled-query [query-binding [model query]] & body)(with-connection [connection-binding connectable] & body)(with-connection [connection-binding connectable] & body)(with-model [model-binding modelable] & body)(with-transaction [conn-binding connectable] & body)cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |