I've got a legacy database that I'm trying to pull into Ecto. In it there's an orders
table which has an order_status_id
column. order_status_id
maps to a set of constants in the legacy system.
I'd like to have the MyApp.Order
struct contain an order_status
field, which has a custom type that converts the integer IDs to meaningful atoms. I've got the custom type working, but I can't figure out how to map a field named order_status
to a column named order_status_id
.
The legacy system is still online and using the database, so changing the DB schema is not an option. Is there any way to get this working?
I'm not sure if it was possible when the question was first asked, but it is possible now (Elixir 1.5). See https://hexdocs.pm/ecto/Ecto.Schema.html
In particular the @field_source_mapper
and :source
options. The :source
option is pretty easy -- just include it in your schema
definition something like this:
schema "orders" do
field :foo, :string, [source: :legacy_foo_db_column]
field :status, :integer, [source: :order_status]
end
In the above example, the existing database table has columns named "legacy_foo_db_column" and "order_status", but internally in the Elixir app, the schema and changeset etc. will use attributes named "foo" and "status"
I think, it is currently not possible to simply associate model field with different column name.
If you really want to use name order_status
, you can create additional virtual field. Those fields are not persisted to database. Then your cast
function should be responsible for changing order_status_id
based on params.
def changeset(model, params \\ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
|> put_order_status_id
end
defp put_order_status_id(changeset) do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{order_status: status}} ->
put_change(changeset, :order_status_id, id_from_status(status))
_ ->
changeset
end
end
In theory, you can also use changeset function to do the opposite operation: set order_status
based on order_status_id
, but it adds complexity.
In my opinion, the best solution would be to simply accept order_status_id
name.