I saw well-intended recommendations not to use schemas in migrations lately. Although the advice of switching to raw SQL is a good one, we don’t have to give up on schemas entirely.
Why do people leave schemas in the first place?
If you reference your application schemas like MyApp.Accounts.User
and MyApp.Accounting.Invoice
in your migrations, things work only until they don’t. Why? When the migration is running, it has to work with the final schema of your application. But migrations change that schema all the time! So there will be a mismatch, and you’ll have to rewrite your old migrations.
What people recommend instead?
A safe way is to resort to pure SQL or call the Repo
module without referencing schemas. That’s a fool-proof way, and I don’t disagree.
So what are you trying to say?
I see this kind of advice missing the real point. It’s never been about schemas.
Define and use schemas as much as you want in migrations; just make sure they are relevant for the specific change only:
defmodule MyApp.Repo.Migrations.AddSubdivisionsToCountries do
use Ecto.Migration
alias MyApp.Repo
defmodule Country do
use Ecto.Schema
schema "countries" do
field(:iso, :string)
end
end
defmodule CountrySubdivision do
use Ecto.Schema
schema "country_subdivisions" do
belongs_to(:country, Country)
end
end
def change do
countries = Repo.all(Country)
for country <- countries do
Repo.insert!(%CountrySubdivision{country_id: country.id, ...})
end
end
end
The real key is to define your schemas representing the schema at the time of the migration being written.
If you want to use schemas for convenience, copy the relevant application schemas over to the migration and ideally make them as minimal as possible (keeping only the mapping you need).
Conclusion?
Schemas are just a tool. You can use them in your Ecto migrations all you want as long as you are not referencing your current application schemas.