Seeding Data
When creating an app, it’s important that we’re able to seed our datastore with some initial data (e.g., for early development work or pre-launch testing purposes).
Fortunately, Phoenix already provides us with a convention for seeding data. By default Phoenix generates a script file for each app at priv/repo/seeds.exs
, which we can use to populate our datastore.
Also note that in order to seed data as in the example below you should have already generated and run the related migration (i.e., Link migration, controller, model, etc.) and updated your router.ex
, as described in the Ecto Models Guide (if you haven’t completed that Guide yet, you should do so before proceeding further).
So in order to seed data, we simply need to add a script to seeds.exs
that uses our datastore to directly add the data we want. As you can see from the comments that Phoenix generated for us in seeds.exs
file, we should follow this pattern:
<%= application_module %>.Repo.insert!(%<%= application_module %>.SomeModel{})
For example, if we were creating an app called Linker and wanted to seed a Link table in our datastore with with a series of links, we could simply add the following script to our seeds.exs
file:
...
alias Linker.Repo
alias Linker.Link
Repo.insert! %Link{
title: "Phoenix Framework",
url: "http://www.phoenixframework.org/"
}
Repo.insert! %Link{
title: "Elixir",
url: "http://elixir-lang.org/"
}
Repo.insert! %Link{
title: "Erlang",
url: "https://www.erlang.org/"
}
...
With this script, we’ve set up some aliases and then progressed through a list of our Links which will each be written to the datastore when we run the seeds.exs
file with mix run:
mix run priv/repo/seeds.exs
Note that if we wanted to delete/scrub all prior data that we seeded in the Link table, we could also include Repo.delete_all Link
in your script immediate above Repo.insert!
We can also create a module to seed our data. The reason this is sometimes advantageous is it allows us to quickly seed from IEx, and also keeps things modular. For example:
defmodule <%= application_name %>.DatabaseSeeder do
alias <%= application_name %>.Repo
alias <%= application_name %>.Link
@titles_list ["Erlang", "Elixir", "Phoenix Framework"] // list of titles
@urls_list ["http://www.erlang.org", "http://www.elixir-lang.org", "http://www.phoenixframework.org"] // list of urls
def insert_link do
Repo.insert! %Link{
title: (@titles_list |> Enum.take_random),
url: (@urls_list |> Enum.take_random)
}
end
def clear do
Repo.delete_all
end
end
(1..100) |> Enum.each(fn _ -> <%= application_name %>.DatabaseSeeder.insert_link end)
Now, we could add links trivially to our database in IEx like so:
$ iex -S mix
iex(1)> <%= application_name %>.DatabaseSeeder.add_link
iex(2)> <%= application_name %>.Link |> <%= application_name %>.Repo.all
#=> [%<%= application_name %>.Link{...}]
This is nice for experimenting in IEx during development in many cases.
Models are Initialized
Conveniently, when following this convention, Phoenix makes sure that our models are appropriately initialized; and as long as we use the bang functions (e.g., insert!
, update!
, etc.), they will also fail if something goes wrong.
This is helpful, since it means that if we make a programming error (e.g., attempting to add a duplicate entry to a table where the field is required to be unique), the data in our database won’t lose its integrity. The database will refuse to execute the query and Ecto will throw an exception, such as:
** (Ecto.ConstraintError) constraint error when attempting to insert model:
Which will be followed by a description of any constraint errors.
In the development environment, we can also modify our script to check for errors before executing the bang functions (e.g., checking for duplicates) to prevent them from failing.