Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ecto Embedded Schemas

Ecto Embedded Schemas

We’ll take a look at how Ecto’s embedded schemas can help us persist and retrieve structured and meaningful data using database JSON columns.

This is a lightning talk I gave at the May 2018 Montreal Elixir Meetup hosted by Mirego.

Rémi Prévost

May 09, 2018
Tweet

More Decks by Rémi Prévost

Other Decks in Technology

Transcript

  1. Ecto Embedded Schemas
    Rémi Prévost Montreal Elixir Meetup
    2018/05/09

    View Slide

  2. View Slide

  3. params = %{
    "author_name" => "Rémi Prévost",
    "published" => true,
    "title" => %{
    "en" => "My new article in english",

    "fr" => "Mon nouvel article en français"
    }
    }
    %App.Blog.Article{}
    |> App.Blog.Article.changeset(params)
    |> App.Repo.insert!()

    View Slide

  4. Schema-less data
    Map → JSON → Map

    View Slide

  5. defmodule App.Blog.Article do
    use Ecto.Schema
    import Ecto.Changeset
    schema "articles" do
    field(:author_name, :string)
    field(:published, :boolean)
    field(:title, :map)
    end
    def changeset(%__MODULE__{} = record, params) do
    record
    |> cast(~w(author_name published title)a, params)
    |> validate_required(~w(author_name title)a)
    |> validate_change(:title, &validate_nested_french_value/2)
    end

    defp validate_nested_french_value(field, %{"fr" => fr}) when fr != "", do: []
    defp validate_nested_french_value(field, _) do
    [{field, "must specify french value"}]
    end
    end

    View Slide

  6. App.Blog.Article
    |> App.Repo.get(1)
    %App.Blog.Article{
    id: 1,
    author_name: "Rémi Prévost",
    published: true,
    title: %{
    en: "My new article in english",

    fr: "Mon nouvel article in english"
    }
    }

    View Slide

  7. defmodule App.Payments.Transaction do
    use Ecto.Schema
    import Ecto.Changeset
    schema "payment_transations" do
    field(:gateway_response, :map)
    belongs_to(:order_id, App.Order)
    end
    def changeset(%__MODULE__{} = record, params) do
    record
    |> cast(~w(order_id gateway_response)a, params)
    |> validate_required(~w(order_id)a)
    end
    end

    View Slide

  8. Embedded schema
    Schema → JSON → Schema

    View Slide

  9. defmodule App.Blog.Article do
    use Ecto.Schema
    import Ecto.Changeset
    schema "articles" do
    field(:author_name, :string)

    field(:published, :boolean)
    embeds_one(:title, App.Translations.Translated)
    end
    def changeset(%__MODULE__{} = record, params) do
    record
    |> cast(~w(author_name published)a, params)
    |> validate_required(~w(author_name)a)
    |> cast_embed(:title, required: true)
    end
    end

    View Slide

  10. defmodule App.Translations.Translated do
    use Ecto.Schema
    import Ecto.Changeset
    embedded_schema do
    field(:en, :string)
    field(:fr, :string)
    end
    def changeset(%__MODULE__{} = record, params) do
    record
    |> cast(~w(en fr)a, params)
    |> validate_required(~w(fr)a)
    end
    end

    View Slide

  11. App.Blog.Article
    |> App.Repo.get(1)
    %App.Blog.Article{
    id: 1,
    author_name: "Rémi Prévost",

    published: true,
    title: %App.Translations.Translated{
    id: "602975a2-bfbd-4c48-9d60-0b955ccc4bc7",
    en: "My new article in english",
    fr: "Mon nouvel article en français"
    }
    }

    View Slide

  12. That’s it!
    Ecto.Schema and Ecto.Changeset are awesome.

    View Slide