Ecto.Schema

Parsed documentation:
View on GitHub
Defines a schema for a model.

A schema is a struct with associated metadata that is persisted to a
repository. Every schema model is also a struct, that means that you work
with models just like you would work with structs.

## Example

    defmodule User do
      use Ecto.Schema

      schema "users" do
        field :name, :string
        field :age, :integer, default: 0
        has_many :posts, Post
      end
    end

By default, a schema will generate a primary key named `id`
of type `:integer` and `belongs_to` associations in the schema will generate
foreign keys of type `:integer`. Those setting can be configured
below.

## Schema attributes

The schema supports some attributes to be set before hand,
configuring the defined schema.

Those attributes are:

  * `@primary_key` - configures the schema primary key. It expects
    a tuple with the primary key name, type (:id or :binary_id) and options. Defaults
    to `{:id, :id, autogenerate: true}`. When set to
    false, does not define a primary key in the model;

  * `@foreign_key_type` - configures the default foreign key type
    used by `belongs_to` associations. Defaults to `:integer`;

  * `@timestamps_opts` - configures the default timestamps type
    used by `timestamps`. Defaults to `[type: Ecto.DateTime, usec: false]`;

  * `@derive` - the same as `@derive` available in `Kernel.defstruct/1`
    as the schema defines a struct behind the scenes;

The advantage of configuring the schema via those attributes is
that they can be set with a macro to configure application wide
defaults.

For example, if your database does not support autoincrementing
primary keys and requires something like UUID or a RecordID, you
configure and use`:binary_id` as your primary key type as follows:

    # Define a module to be used as base
    defmodule MyApp.Model do
      defmacro __using__(_) do
        quote do
          use Ecto.Model
          @primary_key {:id, :binary_id, autogenerate: true}
          @foreign_key_type :binary_id
        end
      end
    end

    # Now use MyApp.Model to define new models
    defmodule MyApp.Comment do
      use MyApp.Model

      schema "comments" do
        belongs_to :post, MyApp.Post
      end
    end

Any models using `MyApp.Model` will get the `:id` field with type
`:binary_id` as primary key. We explain what the `:binary_id` type
entails in the next section.

The `belongs_to` association on `MyApp.Comment` will also define
a `:post_id` field with `:binary_id` type that references the `:id`
field of the `MyApp.Post` model.

## Primary keys

Ecto supports two ID types, called `:id` and `:binary_id` which are
often used as the type for primary keys and associations.

The `:id` type is used when the primary key is an integer while the
`:binary_id` is used when the primary key is in binary format, which
may be `Ecto.UUID` for databases like PostgreSQL and MySQL, or some
specific ObjectID or RecordID often imposed by NoSQL databases.

In both cases, both types have their semantics specified by the
underlying adapter/database. For example, if you use the `:id`
type with `:autogenerate`, it means the database will be responsible
for auto-generation the id if it supports it.

Similarly, the `:binary_id` type may be generated in the adapter
for cases like UUID but it may also be handled by the database if
required. In any case, both scenarios are handled transparently by
Ecto.

Besides `:id` and `:binary_id`, which are often used by primary
and foreign keys, Ecto provides a huge variety of types to be used
by the remaining columns.

## Types and casting

When defining the schema, types need to be given. Types are split
in two categories, primitive types and custom types.

### Primitive types

The primitive types are:

Ecto type               | Elixir type             | Literal syntax in query
:---------------------- | :---------------------- | :---------------------
`:id`                   | `integer`               | 1, 2, 3
`:binary_id`            | `binary`                | `<<int, int, int, ...>>`
`:integer`              | `integer`               | 1, 2, 3
`:float`                | `float`                 | 1.0, 2.0, 3.0
`:boolean`              | `boolean`               | true, false
`:string`               | UTF-8 encoded `string`  | "hello"
`:binary`               | `binary`                | `<<int, int, int, ...>>`
`{:array, inner_type}`  | `list`                  | `[value, value, value, ...]`
`:decimal`              | [`Decimal`](https://github.com/ericmj/decimal)
`:map`                  | `map`

**Note:** For the `:array` type, replace `inner_type` with one of
the valid types, such as `:string`.

### Custom types

Besides providing primitive types, Ecto allows custom types to be
implemented by developers, allowing Ecto behaviour to be extended.

A custom type is a module that implements the `Ecto.Type` behaviour.
By default, Ecto provides the following custom types:

Custom type             | Database type           | Elixir type
:---------------------- | :---------------------- | :---------------------
`Ecto.DateTime`         | `:datetime`             | `%Ecto.DateTime{}`
`Ecto.Date`             | `:date`                 | `%Ecto.Date{}`
`Ecto.Time`             | `:time`                 | `%Ecto.Time{}`
`Ecto.UUID`             | `:uuid`                 | "uuid-string"

Read the `Ecto.Type` documentation for more information on implementing
your own types.

### The map type

The map type allows developers to store an Elixir map directly
in the database:

    # In your migration
    create table(:users) do
      add :data, :map
    end

    # In your model
    field :data, :map

    # Now in your code
    %User{data: %{"foo" => "bar"}} |> Repo.insert!
    %User{data: %{"foo" => value}} = Repo.one(User)
    value #=> "bar"

Keep in mind that we advise the map keys to be strings or integers
instead of atoms. Atoms may be accepted depending on how maps are
serialized but the database will always return atom keys as strings
due to security reasons.

In order to support maps, different databases may employ different
techniques. For example, PostgreSQL will store those values in jsonb
fields, allowing you to even query parts of it. MySQL and MSSQL, on
the other hand, do not yet provide a JSON type, so the value will be
stored in a text field.

For maps to work in such databases, Ecto will need a JSON library.
By default Ecto will use [Poison](http://github.com/devinus/poison)
which needs to be added your deps in `mix.exs`:

    {:poison, "~> 1.0"}

You can however tell Ecto to use any other library by configuring it:

    config :ecto, :json_library, YourLibraryOfChoice

### Casting

When directly manipulating the struct, it is the responsibility of
the developer to ensure the field values have the proper type. For
example, you can create a user struct with an invalid value
for `age`:

    iex> user = %User{age: "0"}
    iex> user.age
    "0"

However, if you attempt to persist the struct above, an error will
be raised since Ecto validates the types when sending them to the
adapter/database.

Therefore, when working and manipulating external data, it is
recommended the usage of `Ecto.Changeset`'s that are able to filter
and properly cast external data:

    changeset = Ecto.Changeset.cast(%User{}, %{"age" => "0"}, [:age], [])
    user = Repo.insert!(changeset)

In fact, `Ecto.Changeset` and custom types provide a powerful
combination to extend Ecto types and queries.

Finally, models can also have virtual fields by passing the
`virtual: true` option. These fields are not persisted to the database
and can optionally not be type checked by declaring type `:any`.

## Reflection

Any schema module will generate the `__schema__` function that can be
used for runtime introspection of the schema:

* `__schema__(:source)` - Returns the source as given to `schema/2`;
* `__schema__(:primary_key)` - Returns a list of the field that is the primary
  key or [] if there is none;
* `__schema__(:fields)` - Returns a list of all non-virtual field names;

* `__schema__(:type, field)` - Returns the type of the given non-virtual field;
* `__schema__(:types)` - Returns a keyword list of all non-virtual
  field names and their type;

* `__schema__(:associations)` - Returns a list of all association field names;
* `__schema__(:association, assoc)` - Returns the association reflection of the given assoc;

* `__schema__(:embeds)` - Returns a list of all embedded field names;
* `__schema__(:embed, embed)` - Returns the embedding reflection of the given embed;

* `__schema__(:read_after_writes)` - Non-virtual fields that must be read back
  from the database after every write (insert or update);

* `__schema__(:autogenerate)` - Non-virtual fields that are auto generated on insert;

* `__schema__(:autogenerate_id)` - Primary key that is auto generated on insert;

Furthermore, both `__struct__` and `__changeset__` functions are
defined so structs and changeset functionalities are available.
No suggestions.
Please help! Open an issue on GitHub if this assessment is incorrect.