### Verify setup with Ecto.Query.first and Friends.Repo.one Source: https://github.com/elixir-ecto/ecto/blob/master/examples/friends/README.md Verifies the setup by fetching the first person using Ecto.Query.first; shows the generated SQL and the resulting struct with debug log. ```elixir iex(1)> Friends.Person |> Ecto.Query.first |> Friends.Repo.one 03:00:00.198 [debug] QUERY OK source="people" db=0.6ms decode=1.2ms queue=1.0ms idle=1384.7ms SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 ORDER BY p0."id" LIMIT 1 [] %Friends.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, id: 1, first_name: "Ryan", last_name: "Bigg", age: 28 } ``` -------------------------------- ### Start primary and replica repositories in supervision tree Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Replicas and dynamic repositories.md Lists all repositories to be started in the supervision tree. This ensures both the primary and all four replicas are running. ```elixir children = [ MyApp.Repo, MyApp.Repo.Replica1, MyApp.Repo.Replica2, MyApp.Repo.Replica3, MyApp.Repo.Replica4 ] ``` -------------------------------- ### Start a dynamic repository with credentials Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Replicas and dynamic repositories.md Starts a repository with a given set of credentials dynamically. The `start_link` function accepts the same options as the database configuration. ```elixir MyApp.Repo.start_link( name: :some_client, hostname: "client.example.com", username: "...", password: "...", pool_size: 1 ) ``` -------------------------------- ### Install dependencies with mix deps.get Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Installs the dependencies declared in mix.exs, including Ecto and Postgrex. ```shell mix deps.get ``` -------------------------------- ### Start an unnamed repository and clean up Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Replicas and dynamic repositories.md Starts a repository with no name, retrieves its PID, and uses it with `put_dynamic_repo`. The `after` block restores the default dynamic repo and stops the new repository for proper cleanup. ```elixir default_dynamic_repo = MyApp.Repo.get_dynamic_repo() {:ok, repo} = MyApp.Repo.start_link( name: nil, hostname: "client.example.com", username: "...", password: "...", pool_size: 1 ) try do MyApp.Repo.put_dynamic_repo(repo) MyApp.Repo.all(Post) after MyApp.Repo.put_dynamic_repo(default_dynamic_repo) Supervisor.stop(repo) end ``` -------------------------------- ### Ecto configuration, schema, and query example Source: https://github.com/elixir-ecto/ecto/blob/master/README.md Complete example showing Ecto configuration, a repository module, a schema, and two query functions. The config sets up a PostgreSQL database connection. The schema defines fields with default types and a default value. The query functions demonstrate both keyword and pipe query syntax. ```elixir # In your config/config.exs file config :my_app, ecto_repos: [Sample.Repo] config :my_app, Sample.Repo, database: "ecto_simple", username: "postgres", password: "postgres", hostname: "localhost", port: "5432" # In your application code defmodule Sample.Repo do use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Postgres end defmodule Sample.Weather do use Ecto.Schema schema "weather" do field :city # Defaults to type :string field :temp_lo, :integer field :temp_hi, :integer field :prcp, :float, default: 0.0 end end defmodule Sample.App do import Ecto.Query alias Sample.{Weather, Repo} def keyword_query do query = from w in Weather, where: w.prcp > 0 or is_nil(w.prcp), select: w Repo.all(query) end def pipe_query do Weather |> where(city: "Kraków") |> order_by(:temp_lo) |> limit(10) |> Repo.all end end ``` -------------------------------- ### Start app via IEx session Source: https://github.com/elixir-ecto/ecto/blob/master/examples/friends/README.md Starts the Friends application inside an IEx session for interactive exploration. ```sh iex -S mix ``` -------------------------------- ### Setup commands for Friends sample app Source: https://github.com/elixir-ecto/ecto/blob/master/examples/friends/README.md Sets up the project dependencies, runs the test suite, and seeds the database for the Friends sample app. ```sh mix deps.get mix test mix run priv/repo/seeds.exs ``` -------------------------------- ### Define RepoCase template with sandbox setup Source: https://github.com/elixir-ecto/ecto/blob/master/guides/testing/Testing with Ecto.md Defines a case template that imports Ecto and Ecto.Query, starts a sandbox owner per test, and stops it on exit. It enables shared mode for non-async tests. ```elixir defmodule MyApp.RepoCase do use ExUnit.CaseTemplate using do quote do alias MyApp.Repo import Ecto import Ecto.Query import MyApp.RepoCase # and any other stuff end end setup tags do pid = Ecto.Adapters.SQL.Sandbox.start_owner!(MyApp.Repo, shared: not tags[:async]) on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end) :ok end end ``` -------------------------------- ### Use with_dynamic_repo with credentials Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Replicas and dynamic repositories.md Uses the `with_dynamic_repo/2` function with a list of credentials and a callback that runs a query. This encapsulates the dynamic connection setup and cleanup. ```elixir credentials = [ hostname: "client.example.com", username: "...", password: "..." ] MyApp.Repo.with_dynamic_repo(credentials, fn -> MyApp.Repo.all(Post) end) ``` -------------------------------- ### Redirect replica repos to primary in test setup Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Replicas and dynamic repositories.md Test setup that redirects each replica repository to use the primary `MyApp.Repo` connection pool. `put_dynamic_repo` is per process, so the value resets for each new process until called again. ```elixir @replicas [ MyApp.Repo.Replica1, MyApp.Repo.Replica2, MyApp.Repo.Replica3, MyApp.Repo.Replica4 ] setup do for replica <- @replicas do replica.put_dynamic_repo(MyApp.Repo) end :ok end ``` -------------------------------- ### Use factory functions in tests Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Test factories.md Shows how to import MyApp.Factory and use build/1, build/2, and insert!/2 in tests, with example outputs for each call. ```elixir import MyApp.Factory build(:post) #=> %MyApp.Post{id: nil, title: "hello world", ...} build(:post, title: "custom title") #=> %MyApp.Post{id: nil, title: "custom title", ...} insert!(:post, title: "custom title") #=> %MyApp.Post{id: ..., title: "custom title"} ``` -------------------------------- ### Start multiple repository processes with different names Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Replicas and dynamic repositories.md Starts two repository processes from the same repository module with different names, each with its own connection pool. The first uses the default process name `MyApp.Repo`; the second is named `:another_instance_of_repo`. ```elixir children = [ MyApp.Repo, {MyApp.Repo, name: :another_instance_of_repo} ] ``` -------------------------------- ### Re-create database with mix ecto.drop, create, migrate Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Re-creates the database and applies migrations before running the example queries. Run these commands in the project root. ```shell mix ecto.drop mix ecto.create mix ecto.migrate ``` -------------------------------- ### Result of Friends.Repo.one for first query Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Example struct returned by Friends.Repo.one for the first query. ```elixir %Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 28, first_name: "Ryan", id: 1, last_name: "Bigg"} ``` -------------------------------- ### Checkout sandbox per test Source: https://github.com/elixir-ecto/ecto/blob/master/guides/testing/Testing with Ecto.md Alternative to using a case template: checks out a sandbox connection directly in each test's setup. ```elixir defmodule MyApp.MyTest do use ExUnit.Case setup do :ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo) end # Tests etc... end ``` -------------------------------- ### Use RepoCase in a test Source: https://github.com/elixir-ecto/ecto/blob/master/guides/testing/Testing with Ecto.md Uses the RepoCase template in a test module to get the sandbox setup and Ecto imports. ```elixir defmodule MyApp.MyTest do use MyApp.RepoCase # Tests etc... end ``` -------------------------------- ### Add Friends.Repo to supervision tree in application.ex Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Adds Friends.Repo to the supervision tree in lib/friends/application.ex, starting the Ecto process that executes queries. ```elixir def start(_type, _args) do children = [ Friends.Repo, ] ... ``` -------------------------------- ### Define with_dynamic_repo function in repository Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Replicas and dynamic repositories.md Defines a `with_dynamic_repo/2` function inside the repository module to encapsulate starting a dynamic repository, running a callback, and cleaning up. It uses `name: nil` and `pool_size: 1` in the start options. ```elixir defmodule MyApp.Repo do use Ecto.Repo, ... def with_dynamic_repo(credentials, callback) do default_dynamic_repo = get_dynamic_repo() start_opts = [name: nil, pool_size: 1] ++ credentials {:ok, repo} = MyApp.Repo.start_link(start_opts) try do MyApp.Repo.put_dynamic_repo(repo) callback.() after MyApp.Repo.put_dynamic_repo(default_dynamic_repo) Supervisor.stop(repo) end end end ``` -------------------------------- ### Result of Friends.Repo.all Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Example list of Friends.Person structs returned by Friends.Repo.all for the three test records. ```elixir [%Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 28, first_name: "Ryan", id: 1, last_name: "Bigg"}, %Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 27, first_name: "John", id: 2, last_name: "Smith"}, %Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 26, first_name: "Jane", id: 3, last_name: "Smith"}] ``` -------------------------------- ### Changeset errors keyword list example Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Example of the errors keyword list returned in a changeset. Each element is a tuple with the field name, the validation message, and a keyword list of options. ```elixir [first_name: {"can't be blank", [validation: :required]}, last_name: {"can't be blank", [validation: :required]}] ``` -------------------------------- ### Run migrations with --prefix option Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Multi tenancy with query prefixes.md Invoke `mix ecto.migrate` once per prefix to migrate data across multiple prefixes. This is one of two approaches for migrating data in a multi-prefix setup. ```bash $ mix ecto.migrate --prefix "prefix_1" $ mix ecto.migrate --prefix "prefix_2" $ mix ecto.migrate --prefix "prefix_3" ... $ mix ecto.migrate --prefix "prefix_128" ``` -------------------------------- ### Query a dynamically started repository with put_dynamic_repo Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Replicas and dynamic repositories.md Sets the dynamic repository to the named client before running a query. Without calling `put_dynamic_repo/1`, `MyApp.Repo.all(Post)` may fail because it defaults to the process named `MyApp.Repo`. ```elixir MyApp.Repo.put_dynamic_repo(:some_client) MyApp.Repo.all(Post) ``` -------------------------------- ### Define test alias for database setup Source: https://github.com/elixir-ecto/ecto/blob/master/guides/testing/Testing with Ecto.md Defines a test alias in mix.exs that runs ecto.create and ecto.migrate before running tests. ```elixir def project do [app: :my_app, ... aliases: aliases()] end defp aliases do [ ... "test": ["ecto.create --quiet", "ecto.migrate", "test"] ] end ``` -------------------------------- ### Successful insert return tuple Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Example of the tuple returned by a successful Friends.Repo.insert/1 call. The :ok atom can be used for pattern matching to ensure the insertion succeeded. ```elixir {:ok, %Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: nil, first_name: nil, id: 1, last_name: nil}} ``` -------------------------------- ### Set up Ecto repo, schema, config, migration, and run initial query Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Multi tenancy with query prefixes.md Sets up a basic Ecto repository, schema, configuration, and migration, then creates the database, runs migrations, and queries the samples table, which returns an empty list. ```elixir # lib/repo.ex defmodule MyApp.Repo do use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Postgres end # lib/sample.ex defmodule MyApp.Sample do use Ecto.Schema schema "samples" do field :name timestamps end end ``` ```elixir # config/config.exs config :my_app, MyApp.Repo, username: "postgres", password: "postgres", database: "demo", hostname: "localhost", pool_size: 10 ``` ```elixir # priv/repo/migrations/20160101000000_create_sample.exs defmodule MyApp.Repo.Migrations.CreateSample do use Ecto.Migration def change do create table(:samples) do add :name, :string timestamps() end end end ``` ```bash $ mix ecto.create $ mix ecto.migrate $ iex -S mix Interactive Elixir - press Ctrl+C to exit iex(1)> MyApp.Repo.all MyApp.Sample [] ``` -------------------------------- ### Create database with mix ecto.create Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Run this command to create the database for the current environment. If successful, the message 'The database for Friends.Repo has been created.' is displayed. If an error occurs, check the configuration in config/config.exs for authentication issues. ```shell mix ecto.create ``` -------------------------------- ### Configure primary and replicas for development/test in config/config.exs Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Replicas and dynamic repositories.md Configures all repositories in config/config.exs for development and test environments, pointing them to the same localhost database with identical credentials. This is suitable when all repositories can share the same database address. ```elixir replicas = [ MyApp.Repo, MyApp.Repo.Replica1, MyApp.Repo.Replica2, MyApp.Repo.Replica3, MyApp.Repo.Replica4 ] for repo <- replicas do config :my_app, repo, username: "postgres", password: "postgres", database: "my_app_prod", hostname: "localhost", pool_size: 10 end ``` -------------------------------- ### Run queries on connection prefix Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Multi tenancy with query prefixes.md Shows basic Repo operations running on the connection's default prefix ("connection_prefix") when no :prefix option is given. The insert and subsequent query return data from that prefix. ```iex iex(1)> alias MyApp.Sample MyApp.Sample iex(2)> MyApp.Repo.all(Sample) [] iex(3)> MyApp.Repo.insert(%Sample{name: "mary"}) {:ok, %MyApp.Sample{...}} iex(4)> MyApp.Repo.all(Sample) [%MyApp.Sample{...}] ``` -------------------------------- ### Query with :prefix option Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Multi tenancy with query prefixes.md Demonstrates the :prefix option on Repo.all/2. Without it, the query runs on the connection prefix; with prefix: "public", it queries the "public" prefix, which is empty. ```iex iex(7)> MyApp.Repo.all(Sample) [%MyApp.Sample{...}] iex(8)> MyApp.Repo.all(Sample, prefix: "public") [] ``` -------------------------------- ### Create initial orgs, posts, and comments tables Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Multi tenancy with foreign keys.md Initial migration for multi-tenant tables. The orgs table uses primary_key: false and defines org_id as the primary key. Posts and comments reference orgs and posts, but this does not guarantee that posts and their comments belong to the same organization. ```elixir create table(:orgs, primary_key: false) do add :org_id, :bigserial, primary_key: true add :name, :string timestamps() end create table(:posts) do add :title, :string add :org_id, references(:orgs, column: :org_id), null: false timestamps() end create table(:comments) do add :body, :string add :org_id, references(:orgs), null: false add :post_id, references(:posts), null: false timestamps() end ``` -------------------------------- ### Form parameters for many_to_many Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Polymorphic associations with many to many.md Example of form parameters that can be sent to a many_to_many association, allowing nested todo_items to be cast and associated. ```elixir %{"todo_list" => %{ "title" => "shipping list", "todo_items" => %{ 0 => %{"description" => "bread"}, 1 => %{"description" => "eggs"}, } }} ``` -------------------------------- ### Configure primary and replicas for production in config/config.exs Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Replicas and dynamic repositories.md Configures each repository in production to connect to a different hostname, mapping MyApp.Repo to prod-primary and each replica to its own prod-replica-N host. This is the production counterpart to the development/test configuration. ```elixir repos = %{ MyApp.Repo => "prod-primary", MyApp.Repo.Replica1 => "prod-replica-1", MyApp.Repo.Replica2 => "prod-replica-2", MyApp.Repo.Replica3 => "prod-replica-3", MyApp.Repo.Replica4 => "prod-replica-4" } for {repo, hostname} <- repos do config :my_app, repo, username: "postgres", password: "postgres", database: "my_app_prod", hostname: hostname, pool_size: 10 end ``` -------------------------------- ### Successful update return value Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Example successful return value of Friends.Repo.update, showing the updated person struct with age 29. ```elixir {:ok, %Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 29, first_name: "Ryan", id: 1, last_name: "Bigg"}} ``` -------------------------------- ### Successful database creation message Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Expected output after running 'mix ecto.create' when the database is created successfully. ```text The database for Friends.Repo has been created. ``` -------------------------------- ### Run migrations with mix ecto.migrate Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Runs the migration to create the 'people' table in the database. To undo the changes, use 'mix ecto.rollback'. ```shell mix ecto.migrate ``` -------------------------------- ### Run Ecto integration tests with ecto_sql Source: https://github.com/elixir-ecto/ecto/blob/master/README.md Run Ecto integration tests by cloning ecto_sql in a sibling directory and setting the ECTO_PATH environment variable to point to the Ecto checkout. ```shell $ cd .. $ git clone https://github.com/elixir-ecto/ecto_sql.git $ cd ecto_sql $ mix deps.get $ ECTO_PATH=../ecto mix test.all ``` -------------------------------- ### Run containerized integration tests with earthly Source: https://github.com/elixir-ecto/ecto/blob/master/README.md Run integration tests in a containerized environment using earthly. ```shell $ earthly -P +all ``` -------------------------------- ### Create empty Friends.Person struct Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Creates a new Friends.Person struct with all fields set to nil. Run in an IEx session started with iex -S mix. ```elixir person = %Friends.Person{} ``` -------------------------------- ### UserProfile changeset with cast and validate_required Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Embedded Schemas.md Defines a changeset for an embedded schema module, requiring the :online and :visibility fields. The example then creates a changeset with those fields present. ```elixir defmodule UserProfile do # ... def changeset(%UserProfile{} = profile, attrs \\ %{}) do profile |> cast(attrs, [:online, :dark_mode, :visibility]) |> validate_required([:online, :visibility]) end end profile = %UserProfile{} UserProfile.changeset(profile, %{online: true, visibility: :public}) ``` -------------------------------- ### Manually construct query with Ecto.Query.from Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Constructs the same query manually using Ecto.Query.from. Requires Ecto.Query to enable its macros. ```elixir require Ecto.Query Ecto.Query.from p in Friends.Person, order_by: [asc: p.id], limit: 1 ``` -------------------------------- ### Trigger Ecto.InvalidChangesetError with insert! Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Example of calling Friends.Repo.insert!/2 with an invalid changeset. It raises an Ecto.InvalidChangesetError exception showing the errors, applied changes, params, and changeset details. ```text Friends.Repo.insert! Friends.Person.changeset(%Friends.Person{}, %{first_name: "Ryan"}) ** (Ecto.InvalidChangesetError) could not perform insert because changeset is invalid. Errors %{last_name: [{"can't be blank", [validation: :required]}]} Applied changes %{first_name: "Ryan"} Params %{"first_name" => "Ryan"} Changeset #Ecto.Changeset< action: :insert, changes: %{first_name: "Ryan"}, errors: [last_name: {"can't be blank", [validation: :required]}], data: #Friends.Person<>, valid?: false > (ecto) lib/ecto/repo/schema.ex:257: Ecto.Repo.Schema.insert!/4 ``` -------------------------------- ### Failed insert return tuple Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Example of the tuple returned when inserting a changeset that fails validation. The :error atom indicates failure and the changeset contains error details. ```elixir {:error, #Ecto.Changeset, valid?: false>} ``` -------------------------------- ### Define repository with Ecto.Adapters.Postgres Source: https://github.com/elixir-ecto/ecto/blob/master/README.md Define your repository module with the PostgreSQL adapter. The `adapter:` option must match the chosen dependency. ```elixir defmodule MyApp.Repo do use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Postgres, ... ``` -------------------------------- ### Define primary and replica repositories in lib/my_app/repo.ex Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Replicas and dynamic repositories.md Defines the primary MyApp.Repo and four read-only replica repositories (MyApp.Repo.Replica1 through Replica4) in lib/my_app/repo.ex. The :read_only option disables write operations like insert and update on replicas, and the replica/0 function returns a random replica. ```elixir defmodule MyApp.Repo do use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Postgres @replicas [ MyApp.Repo.Replica1, MyApp.Repo.Replica2, MyApp.Repo.Replica3, MyApp.Repo.Replica4 ] def replica do Enum.random(@replicas) end for repo <- @replicas do defmodule repo do use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Postgres, read_only: true end end end ``` -------------------------------- ### Library.Lending schema for lendings table Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Aggregates and subqueries.md Defines the Lending schema for the lendings table with belongs_to associations to Book and Visitor, which define book_id and visitor_id fields. This schema is used in the subsequent subquery example. ```elixir defmodule Library.Lending do use Ecto.Schema schema "lendings" do belongs_to :book, MyApp.Book # defines book_id belongs_to :visitor, MyApp.Visitor # defines visitor_id end end ``` -------------------------------- ### Define Person and Relationship schemas for self-referencing many_to_many Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Self-referencing many to many.md Defines the Person schema with two many_to_many associations (relationships and reverse_relationships) using the Relationship join schema. The Relationship schema stores person_id and relation_id fields. This setup allows symmetric relationships between people. ```elixir defmodule MyApp.Accounts.Person do use Ecto.Schema alias MyApp.Accounts.Person alias MyApp.Relationships.Relationship schema "people" do field :name, :string many_to_many :relationships, Person, join_through: Relationship, join_keys: [person_id: :id, relation_id: :id] many_to_many :reverse_relationships, Person, join_through: Relationship, join_keys: [relation_id: :id, person_id: :id] timestamps() end end defmodule MyApp.Relationships.Relationship do use Ecto.Schema schema "relationships" do field :person_id, :id field :relation_id, :id timestamps() end end ``` -------------------------------- ### Generate a new Elixir application with --sup Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Generates a new Elixir application with a supervision tree, which is required for Ecto later. ```shell mix new friends --sup ``` -------------------------------- ### Inline embedded schema with custom changeset function Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Embedded Schemas.md Defines an embedded schema inline within the parent schema using embeds_one with a block, and provides a custom changeset function for the embedded data via cast_embed with the :with option. The example shows a valid changeset. ```elixir defmodule User do use Ecto.Schema schema "users" do field :full_name, :string field :email, :string field :avatar_url, :string field :confirmed_at, :naive_datetime embeds_one :profile, Profile do field :online, :boolean field :dark_mode, :boolean field :visibility, Ecto.Enum, values: [:public, :private, :friends_only] end timestamps() end def changeset(%User{} = user, attrs \\ %{}) do user |> cast(attrs, [:full_name, :email]) |> cast_embed(:profile, required: true, with: &profile_changeset/2) end def profile_changeset(profile, attrs \\ %{}) do profile |> cast(attrs, [:online, :dark_mode, :visibility]) |> validate_required([:online, :visibility]) end end changeset = User.changeset(%User{}, %{profile: %{online: true, visibility: :public}}) changeset.valid? # => true ``` -------------------------------- ### Configure Friends.Repo database connection in config.exs Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Database connection configuration for the Friends.Repo in config/config.exs. If the default credentials don't work, try removing username/password or setting them to "postgres". For non-standard ports, add port: 15432. ```elixir config :friends, Friends.Repo, database: "friends", username: "user", password: "pass", hostname: "localhost" ``` -------------------------------- ### Define TodoList, TodoListItem, and TodoItem schemas with has_many :through Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Polymorphic associations with many to many.md Defines three Ecto schemas: MyApp.TodoList with has_many :todo_list_items and has_many :todo_items through :todo_list_items, MyApp.TodoListItem with belongs_to associations, and MyApp.TodoItem with a description field. This setup enables polymorphic todo items via an intermediate schema. ```elixir defmodule MyApp.TodoList do use Ecto.Schema schema "todo_lists" do field :title has_many :todo_list_items, MyApp.TodoListItem has_many :todo_items, through: [:todo_list_items, :todo_item] timestamps() end end defmodule MyApp.TodoListItem do use Ecto.Schema schema "todo_list_items" do belongs_to :todo_list, MyApp.TodoList belongs_to :todo_item, MyApp.TodoItem timestamps() end end defmodule MyApp.TodoItem do use Ecto.Schema schema "todo_items" do field :description timestamps() end end ``` -------------------------------- ### Querying embedded data with Ecto.Query.from Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Embedded Schemas.md Queries embedded data using Ecto.Query.from with bracket access on the embedded field. Shows selecting specific embedded fields and filtering by an embedded value, with example outputs. In databases storing :map as JSONB (like Postgres), Ecto constructs jsonpath queries automatically. ```elixir user_changeset = User.changeset(%User{}, %{profile: %{online: true, visibility: :public}}) {:ok, _user} = Repo.insert(user_changeset) (Ecto.Query.from u in User, select: {u.profile["online"], u.profile["visibility"]}) |> Repo.one # => {true, "public"} (Ecto.Query.from u in User, select: u.profile, where: u.profile["visibility"] == ^:public) |> Repo.all # => [ # %UserProfile{ # id: "...", # online: true, # dark_mode: nil, # visibility: :public # } #] ``` -------------------------------- ### Add PostgreSQL dependencies to mix.exs Source: https://github.com/elixir-ecto/ecto/blob/master/README.md Add Ecto SQL and the PostgreSQL adapter to your mix.exs dependencies. Run `mix deps.get` after adding. ```elixir defp deps do [ {:ecto_sql, "~> 3.0"}, {:postgrex, ">= 0.0.0"} ] end ``` -------------------------------- ### Clone and test Ecto Source: https://github.com/elixir-ecto/ecto/blob/master/README.md Clone the Ecto repository, fetch dependencies, and run the test suite. Note that `mix test` does not run tests in the `integration_test` folder. ```shell $ git clone https://github.com/elixir-ecto/ecto.git $ cd ecto $ mix deps.get $ mix test ``` -------------------------------- ### Build dynamic Ecto query with filter_order_by and filter_where Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Dynamic queries.md Builds a dynamic Ecto query by composing small functions. `filter/1` interpolates the results of `filter_order_by/1` and `filter_where/1` into the query. `filter_order_by/1` pattern matches on the `order_by` parameter to return a list of dynamic expressions, defaulting to an empty list. `filter_where/1` uses `Enum.reduce/3` starting with `dynamic(true)` and refines it with conditions for `author`, `category`, and `published_at` parameters, ignoring unknown keys. ```elixir def filter(params) do Post |> order_by(^filter_order_by(params["order_by"])) |> where(^filter_where(params)) end def filter_order_by("published_at_desc"), do: [desc: dynamic([p], p.published_at)] def filter_order_by("published_at"), do: [asc: dynamic([p], p.published_at)] def filter_order_by(_), do: [] def filter_where(params) do Enum.reduce(params, dynamic(true), fn {"author", value}, dynamic -> dynamic([p], ^dynamic and p.author == ^value) {"category", value}, dynamic -> dynamic([p], ^dynamic and p.category == ^value) {"published_at", value}, dynamic -> dynamic([p], ^dynamic and p.published_at > ^value) {_, _}, dynamic -> # Not a where parameter dynamic end) end ``` -------------------------------- ### Create schema manually and run migrations successfully Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Multi tenancy with query prefixes.md Shows that migrations fail with `invalid_schema_name` because the custom prefix does not exist. You must create the schema manually with `psql`, then migrations and queries succeed. ```bash $ mix ecto.migrate ** (Postgrex.Error) ERROR (invalid_schema_name): no schema has been selected to create in ``` ```bash $ psql -d demo_dev -c "CREATE SCHEMA connection_prefix" ``` ```bash $ mix ecto.migrate $ iex -S mix Interactive Elixir - press Ctrl+C to exit iex(1)> MyApp.Repo.all MyApp.Sample [] ``` -------------------------------- ### Execute first query with Friends.Repo.one Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Executes the first query and retrieves a single record. Friends.Repo.one returns a struct or nil if no record matches. ```elixir Friends.Person |> Ecto.Query.first |> Friends.Repo.one ``` -------------------------------- ### Create test factory module in test/support/factory.ex Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Test factories.md Defines a factory module with build/1 clauses for :post, :comment, :post_with_comments, and :user, plus build/2 and insert!/2 convenience functions. Uses System.unique_integer() to generate unique email and username fields; use System.unique_integer([:positive]) for strictly positive numbers. ```elixir defmodule MyApp.Factory do alias MyApp.Repo # Factories def build(:post) do %MyApp.Post{title: "hello world"} end def build(:comment) do %MyApp.Comment{body: "good post"} end def build(:post_with_comments) do %MyApp.Post{ title: "hello with comments", comments: [ build(:comment, body: "first"), build(:comment, body: "second") ] } end def build(:user) do %MyApp.User{ email: "hello#{System.unique_integer()}", username: "hello#{System.unique_integer()}" } end # Convenience API def build(factory_name, attributes) do factory_name |> build() |> struct!(attributes) end def insert!(factory_name, attributes \\ []) do factory_name |> build(attributes) |> Repo.insert!() end end ``` -------------------------------- ### Enable Ecto formatter rules in .formatter.exs Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Updates .formatter.exs to include Ecto formatter rules via import_deps, so mix format applies Ecto's formatting. ```elixir [ # Add this line to enable Ecto formatter rules import_deps: [:ecto, :ecto_sql], # Default Elixir project rules inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] ] ``` -------------------------------- ### Optimize get_or_insert_tag with Repo.get_by Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Constraints and Upserts.md Optimized version that first checks for an existing tag with Repo.get_by, then inserts only if not found. Performs 1 query for existing tags, 2 for new tags, and possibly 3 under race conditions. ```elixir defp get_or_insert_tag(name) do Repo.get_by(MyApp.Tag, name: name) || maybe_insert_tag(name) end defp maybe_insert_tag(name) do %Tag{} |> Ecto.Changeset.change(name: name) |> Ecto.Changeset.unique_constraint(:name) |> Repo.insert |> case do {:ok, tag} -> tag {:error, _} -> Repo.get_by!(MyApp.Tag, name: name) end end ``` -------------------------------- ### Configure IPv6 socket options Source: https://github.com/elixir-ecto/ecto/blob/master/README.md Enable IPv6 support by adding `socket_options: [:inet6]` to the repository configuration block. ```elixir import Mix.Config config :my_app, MyApp.Repo, hostname: "db12.dc0.comp.any", socket_options: [:inet6], ... ``` -------------------------------- ### Update and delete without schema Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Schemaless queries.md Shows how to update and delete posts using a schemaless query with update_all and delete_all. ```elixir # Use the ID to trigger updates post = from p in "posts", where: [id: ^id] # Update the title for all matching posts {1, _} = MyApp.Repo.update_all post, set: [title: "new title"] # Delete all matching posts {1, _} = MyApp.Repo.delete_all post ``` -------------------------------- ### Configure test environment with Ecto SQL Sandbox Source: https://github.com/elixir-ecto/ecto/blob/master/guides/testing/Testing with Ecto.md Configures the test environment to use the Ecto SQL Sandbox pool with a Postgres database. Place this in config/test.exs. ```elixir import Config config :my_app, MyApp.Repo, username: "postgres", password: "postgres", database: "myapp_test", hostname: "localhost", pool: Ecto.Adapters.SQL.Sandbox ``` -------------------------------- ### Create tables and join tables in migration Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Polymorphic associations with many to many.md Migration that creates the main tables (todo_lists, projects, todo_items) and the join tables (todo_list_items, project_items) with foreign key references. The join tables use primary_key: false and omit timestamps, as noted in the comments. ```elixir create table("todo_lists") do add :title timestamps() end create table("projects") do add :name timestamps() end create table("todo_items") do add :description timestamps() end # Primary key and timestamps are not required if # using many_to_many without schemas create table("todo_list_items", primary_key: false) do add :todo_item_id, references(:todo_items) add :todo_list_id, references(:todo_lists) # timestamps() end # Primary key and timestamps are not required if # using many_to_many without schemas create table("project_items", primary_key: false) do add :todo_item_id, references(:todo_items) add :project_id, references(:projects) # timestamps() end ``` -------------------------------- ### Generate Ecto repository with mix ecto.gen.repo Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Generates the configuration and module for a repository named Friends.Repo, including database connection settings. ```shell mix ecto.gen.repo -r Friends.Repo ``` -------------------------------- ### Add ecto_repos configuration to config.exs Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Adds this line to config/config.exs to tell the application about the repo, enabling commands like mix ecto.create. ```elixir config :friends, ecto_repos: [Friends.Repo] ``` -------------------------------- ### Debug log for Friends.Repo.one query Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Debug log showing the SQL query executed when calling Friends.Repo.one without a limit. ```shell [timestamp] [debug] SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 [] OK query=1.8ms ``` -------------------------------- ### Fetch records with Ecto.Query.from Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Fetches the same records using the longer query syntax with Ecto.Query.from. ```elixir Ecto.Query.from(p in Friends.Person, where: p.last_name == "Smith") |> Friends.Repo.all ``` -------------------------------- ### Import test config in config.exs Source: https://github.com/elixir-ecto/ecto/blob/master/guides/testing/Testing with Ecto.md Imports the environment-specific configuration file (e.g., test.exs) at the bottom of config/config.exs. ```elixir import_config "#{config_env()}.exs" ``` -------------------------------- ### Dynamic data structures in pipe query Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Dynamic queries.md Demonstrates dynamically building query parts as data structures and interpolating them with `^`. The `where` keyword list becomes equality checks; order-based comparisons still require explicit expressions. ```elixir where = [author: "José", category: "Elixir"] order_by = [desc: :published_at] Post |> where(^where) |> where([p], p.published_at > ^minimum_date) |> order_by(^order_by) ``` -------------------------------- ### Query a random replica with MyApp.Repo.replica().all/1 Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Replicas and dynamic repositories.md Performs a read operation by calling the replica/0 function to select a random replica and then running the query against it. Use this for read operations while writes go to the primary repository. ```elixir MyApp.Repo.replica().all(query) ``` -------------------------------- ### Set default_dynamic_repo per environment in repo definition Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Replicas and dynamic repositories.md Defines replica repositories with `:default_dynamic_repo` set to `MyApp.Repo` only in the test environment; otherwise it defaults to the replica itself. Place this in `lib/my_app/repo.ex`. ```elixir for repo <- @replicas do default_dynamic_repo = if Mix.env() == :test do MyApp.Repo else repo end defmodule repo do use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Postgres, read_only: true, default_dynamic_repo: default_dynamic_repo end end ``` -------------------------------- ### Keyword and pipe query styles with data structures Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Dynamic queries.md Shows two equivalent query styles: keyword-based `from` and pipe-based `where`/`order_by`. Both use data structures for simple conditions and a pinned variable for the date comparison. ```elixir from p in Post, where: [author: "José", category: "Elixir"], where: p.published_at > ^minimum_date, order_by: [desc: :published_at] ``` ```elixir Post |> where(author: "José", category: "Elixir") |> where([p], p.published_at > ^minimum_date) |> order_by(desc: :published_at) ``` -------------------------------- ### Insert test data with Friends.Repo.insert Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Inserts three test people into the database using Friends.Repo.insert. No changeset is used, so data is inserted without validation. ```elixir people = [ %Friends.Person{first_name: "Ryan", last_name: "Bigg", age: 28}, %Friends.Person{first_name: "John", last_name: "Smith", age: 27}, %Friends.Person{first_name: "Jane", last_name: "Smith", age: 26}, ] Enum.each(people, fn (person) -> Friends.Repo.insert(person) end) ``` -------------------------------- ### Generate migration with mix ecto.gen.migration create_people Source: https://github.com/elixir-ecto/ecto/blob/master/guides/introduction/Getting Started.md Generates a new migration file in priv/repo/migrations. The generated file is empty by default. ```shell mix ecto.gen.migration create_people ``` -------------------------------- ### Inspect struct prefix with Ecto.get_meta Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Multi tenancy with query prefixes.md Shows that a struct returned by a query carries its prefix metadata. Here Ecto.get_meta/2 returns nil because no prefix was specified, so the connection default is used. ```iex iex(9)> [sample] = MyApp.Repo.all(Sample) [%MyApp.Sample{}] iex(10)> Ecto.get_meta(sample, :prefix) nil ``` -------------------------------- ### Set search_path parameter and observe undefined_table error Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Multi tenancy with query prefixes.md Configures the repository to use a custom search path with `:search_path` in `:parameters`. Running the same query now fails with `undefined_table` because the samples table does not exist under the new prefix. ```elixir config :my_app, MyApp.Repo, username: "postgres", password: "postgres", database: "demo_dev", hostname: "localhost", pool_size: 10, parameters: [search_path: "connection_prefix,public"] ``` ```bash $ iex -S mix Interactive Elixir - press Ctrl+C to exit iex(1)> MyApp.Repo.all MyApp.Sample ** (Postgrex.Error) ERROR (undefined_table): relation "samples" does not exist ``` -------------------------------- ### Migrate multiple prefixes in a single migration Source: https://github.com/elixir-ecto/ecto/blob/master/guides/howtos/Multi tenancy with query prefixes.md Define a migration that loops over prefixes and creates tables with a specific prefix. The `flush()` call executes commands on the current prefix before moving to the next. ```elixir defmodule MyApp.Repo.Migrations.CreateSample do use Ecto.Migration def change do for i <- 1..128 do prefix = "prefix_#{i}" create table(:samples, prefix: prefix) do add :name, :string timestamps() end # Execute the commands on the current prefix # before moving on to the next prefix flush() end end end ```