Ecto.Repo.insert
insert
, go back to Ecto.Repo module for more information.
Specs
insert( struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(), opts :: Keyword.t() ) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}
Inserts a struct defined via Ecto.Schema
or a changeset.
In case a struct is given, the struct is converted into a changeset with all non-nil fields as part of the changeset.
In case a changeset is given, the changes in the changeset are merged with the struct fields, and all of them are sent to the database.
It returns {:ok, struct}
if the struct has been successfully
inserted or {:error, changeset}
if there was a validation
or a known constraint error.
Options
:returning
- selects which fields to return. It accepts a list of fields to be returned from the database. Whentrue
, returns all fields. Whenfalse
, no extra fields are returned. It will always include all fields inread_after_writes
as well as any autogenerated id. Not all databases support this option and it may not be available during upserts. See the "Upserts" section for more information.:prefix
- The prefix to run the query on (such as the schema path in Postgres or the database in MySQL). This overrides the prefix set in the query and any@schema_prefix
set any schemas. Also, the@schema_prefix
for the parent record will override all default@schema_prefix
s set in any child schemas for associations.:on_conflict
- It may be one of:raise
(the default),:nothing
,:replace_all
,{:replace_all_except, fields}
,{:replace, fields}
, a keyword list of update instructions or anEcto.Query
query for updates. See the "Upserts" section for more information.:conflict_target
- A list of column names to verify for conflicts. It is expected those columns to have unique indexes on them that may conflict. If none is specified, the conflict target is left up to the database. It may also be{:unsafe_fragment, binary_fragment}
to pass any expression to the database without any sanitization, this is useful for partial index or index with expressions, such asON CONFLICT (coalesce(firstname, ""), coalesce(lastname, ""))
.:stale_error_field
- The field where stale errors will be added in the returning changeset. This option can be used to avoid raisingEcto.StaleEntryError
.:stale_error_message
- The message to add to the configured:stale_error_field
when stale errors happen, defaults to "is stale".
See the "Shared options" section at the module documentation for more options.
Examples
A typical example is calling MyRepo.insert/1
with a struct
and acting on the return value:
case MyRepo.insert %Post{title: "Ecto is great"} do
{:ok, struct} -> # Inserted with success
{:error, changeset} -> # Something went wrong
end
Upserts
insert/2
provides upserts (update or inserts) via the :on_conflict
option. The :on_conflict
option supports the following values:
:raise
- raises if there is a conflicting primary key or unique index:nothing
- ignores the error in case of conflicts:replace_all
- replace all values on the existing row with the values in the schema/changeset, including fields not explicitly set in the changeset, such as IDs and autogenerated timestamps (inserted_at
andupdated_at
). Do not use this option if you have auto-incrementing primary keys, as they will also be replaced. You most likely want to use{:replace_all_except, [:id]}
or{:replace, fields}
explicitly instead. This option requires a schema{:replace_all_except, fields}
- same as above except the given fields are not replaced. This option requires a schema{:replace, fields}
- replace only specific columns. This option requires:conflict_target
- a keyword list of update instructions - such as the one given to
update_all/3
, for example:[set: [title: "new title"]]
- an
Ecto.Query
that will act as anUPDATE
statement, such as the one given toupdate_all/3
. If the struct cannot be found,Ecto.StaleEntryError
will be raised.
Upserts map to "ON CONFLICT" on databases like Postgres and "ON DUPLICATE KEY" on databases such as MySQL.
As an example, imagine :title
is marked as a unique column in
the database:
{:ok, inserted} = MyRepo.insert(%Post{title: "this is unique"})
Now we can insert with the same title but do nothing on conflicts:
{:ok, ignored} = MyRepo.insert(%Post{title: "this is unique"}, on_conflict: :nothing)
assert ignored.id == nil
Because we used on_conflict: :nothing
, instead of getting an error,
we got {:ok, struct}
. However the returned struct does not reflect
the data in the database. One possible mechanism to detect if an
insert or nothing happened in case of on_conflict: :nothing
is by
checking the id
field. id
will be nil if the field is autogenerated
by the database and no insert happened.
For actual upserts, where an insert or update may happen, the situation is slightly more complex, as the database does not actually inform us if an insert or update happened. Let's insert a post with the same title but use a query to update the body column in case of conflicts:
# In Postgres (it requires the conflict target for updates):
on_conflict = [set: [body: "updated"]]
{:ok, updated} = MyRepo.insert(%Post{title: "this is unique"},
on_conflict: on_conflict, conflict_target: :title)
# In MySQL (conflict target is not supported):
on_conflict = [set: [title: "updated"]]
{:ok, updated} = MyRepo.insert(%Post{id: inserted.id, title: "updated"},
on_conflict: on_conflict)
In the examples above, even though it returned :ok
, we do not know
if we inserted new data or if we updated only the :on_conflict
fields.
In case an update happened, the data in the struct most likely does
not match the data in the database. For example, autogenerated fields
such as inserted_at
will point to now rather than the time the
struct was actually inserted.
If you need to guarantee the data in the returned struct mirrors the database, you have three options:
Use
on_conflict: :replace_all
, although that will replace all fields in the database with the ones in the struct/changeset, including autogenerated fields such asinserted_at
andupdated_at
:MyRepo.insert(%Post{title: "this is unique"}, on_conflict: :replace_all, conflict_target: :title)
Specify
read_after_writes: true
in your schema for choosing fields that are read from the database after every operation. Or passreturning: true
toinsert
to read all fields back:MyRepo.insert(%Post{title: "this is unique"}, returning: true, on_conflict: on_conflict, conflict_target: :title)
Alternatively, read the data again from the database in a separate query. This option requires the primary key to be generated by the database:
{:ok, updated} = MyRepo.insert(%Post{title: "this is unique"}, on_conflict: on_conflict) Repo.get(Post, updated.id)
Because of the inability to know if the struct is up to date or not,
inserting a struct with associations and using the :on_conflict
option
at the same time is not recommended, as Ecto will be unable to actually
track the proper status of the association.