Providing Data Directly
View SourceWhen you already have your rows or context values in hand, Blink's put_context
and put_table helpers let you add them to a seeder without defining table/2
or context/2 callbacks.
In this guide, we will:
- Add context values with
put_context - Add tables with
put_table - Mix eager
put_*helpers with callback-basedwith_*
When to use put* vs with*
Use with_table/2 and with_context/2 when data is computed inside callbacks —
for example, when a table depends on rows declared earlier. Reach for
put_table and put_context when the data is already available at the call
site, with no callback needed. Both helpers are imported by use Blink, so you
call them unqualified.
Adding context with put_context
put_context/3 stores a single value under a key. Context is auxiliary data: it
is never inserted, but it stays available to later callbacks via
seeder.context.
defmodule Blog.Seeder do
use Blink
def call(users) do
new()
|> put_context(:generated_at, DateTime.utc_now())
|> put_table("users", users)
|> run(Blog.Repo)
end
endTo add several values at once, pass a keyword list to put_context/2. Pairs are
applied in order:
new()
|> put_context(user_id: user_id, project_indices: project_indices)Adding tables with put_table
put_table/3 adds rows under a table name. rows may be a list or a stream,
and a stream is stored without being materialized:
new()
|> put_table("users", users)
|> run(Blog.Repo)Add several tables at once with put_table/2. List order becomes insertion
order, so declare parents before children:
new()
|> put_table(users: users, posts: posts, comments: comments)
|> run(Blog.Repo)For per-table options — or a large table you want to stream — use put_table/4.
It accepts the same per-table :batch_size and :max_concurrency options as
with_table/4, and per-table values override the ones passed to run/3. The
keyword form cannot carry options:
events = Stream.map(1..1_000_000, fn i -> %{id: i, name: "Event #{i}"} end)
new()
|> put_table("users", users)
|> put_table("events", events, batch_size: 1_000, max_concurrency: 2)
|> run(Blog.Repo)Mixing put_* with callbacks
Eager and callback-based declarations interoperate. A later table/2 callback
reads earlier rows and context off the seeder:
def call(users) do
new()
|> put_context(:tenant_id, 42)
|> put_table("users", users)
|> with_table("posts")
|> run(Blog.Repo)
end
def table(seeder, "posts") do
Enum.map(seeder.tables["users"], fn user ->
%{title: "Welcome, #{user.name}", user_id: user.id, tenant_id: seeder.context[:tenant_id]}
end)
endError handling
put_context and put_table raise ArgumentError when:
- A key or table name already exists.
- The same key appears twice within one keyword list, e.g.
put_table(dup: [], dup: []).
Detection is string-normalized, so a string name and the atom keyword form count as the same table:
# ** (ArgumentError) key already exists in `:tables` of Seeder: users
new()
|> put_table("users", users)
|> put_table(users: more_users)The keyword /2 forms are atom-keyed and carry no options. Use /3 or /4 for
string table names, per-table options, or to add a single table — a bare list of
rows is treated as pairs and will not work.
Summary
In this guide, we learned how to:
- Add context values with
put_context/2andput_context/3 - Add tables with
put_table/2,put_table/3, andput_table/4 - Combine eager
put_*helpers with callback-basedwith_table/2andwith_context/2