Flame_Phoenix
Flame_Phoenix

Reputation: 17584

How to have optional values for an ecto embedded schema?

Background

I am trying to create an embedded schema that needs to save information about orders. Sometimes orders have no customer_id and that is fine (for the application).

defmodule Order do
  use Ecto.Schema

  embedded_schema do
    field(:customer_id, :integer | nil)
    field(:stage, enum: [:bought | :in_shop_cart | :canceled])
    field(:details, :string)
  end
end

Problem

The issue here is that I get a compile error for customer_id:

(CompileError) misplaced operator |/2

The | operator is typically used between brackets as the cons operator:

    [head | tail]

where head is a single element and the tail is the remaining of a list.
It is also used to update maps and structs, via the %{map | key: value} notation,
and in typespecs, such as @type and @spec, to express the union of two types

I understand the issue here is the use of :integer | nil. This is not a list, so the advice I get is not usable.

Question

How can I represent optional values in my embedded schema ? (specifically :something | nil)

Upvotes: 0

Views: 1011

Answers (2)

Everett
Everett

Reputation: 9578

The pipe operator | is used to separate the head of a list from its tail (as the error mentions) and it gets used in @spec definitions when defining allowed inputs/outputs.

Normally, the migrations are where the nullable/not-nullable definition really matters, but with a virtual schema, I think you're fine just specifying the data type (e.g. :integer). If there's no value, a nil will be used in its place (similar to how a struct's fields have nil values unless defaults were defined). In other words

field(:customer_id, :integer)
# is the same as
field(:customer_id, :integer, default: nil)

If you wanted to indicate some "non-nullable" field in an Ecto schema, Set the :default option appropriate, e.g.

field(:details, :string, default: "")

Upvotes: 2

Zain
Zain

Reputation: 41

First of all you can't use | in schema like this.

As far as the first case is concerned like in customer_id, you can use default option to set default a default value if the value isn't provided.

In the second case of stage You can use Ecto's Enum to restrict only specific values to be entered in the db. You can do something like this:

field :status, Ecto.Enum, values: [:foo, :bar, :baz]

Here is some useful documentaion for further reading about Ecto Enum

Upvotes: 1

Related Questions