Skip to content

Commit 5f0493c

Browse files
committed
Add examples of migrations to Ecto Association cheatsheets
1 parent b49897f commit 5f0493c

File tree

3 files changed

+128
-18
lines changed

3 files changed

+128
-18
lines changed

guides/cheatsheets/associations.cheatmd

+88-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Associations
22

3-
In this document, "Internal data" represents data or logic hardcoded into your Elixir code. "External data" means data that comes from the user via forms, APIs, and often need to be normalized, pruned, and validated via `Ecto.Changeset`.
3+
In this document, "Internal data" represents data or logic hardcoded into your Elixir code. "External data" means data that comes from the user via forms, APIs, and often need to be normalized, pruned, and validated via `Ecto.Changeset`. We also include examples of migrations, according to [`EctoSQL`](https://hexdocs.pm/ecto_sql).
44

55
## Has many / belongs to
66
{: .col-2}
@@ -33,6 +33,33 @@ defmodule Character do
3333
end
3434
```
3535

36+
### The migration
37+
38+
```elixir
39+
defmodule MyApp.Migrations.CreateMoviesAndCharacters do
40+
use Ecto.Migration
41+
42+
def change do
43+
create table("movies") do
44+
add :title, :string, null: false
45+
add :release_date, :date
46+
timestamps()
47+
end
48+
49+
# The foreign key is in the belongs_to schema
50+
create table("characters") do
51+
add :name, :string, null: false
52+
add :age, :integer
53+
add :movie_id,
54+
references(:movies, on_delete: :delete_all),
55+
null: false
56+
57+
timestamps()
58+
end
59+
end
60+
end
61+
```
62+
3663
## Has one / belongs to
3764
{: .col-2}
3865

@@ -63,6 +90,32 @@ defmodule Screenplay do
6390
end
6491
```
6592

93+
### The migration
94+
95+
```elixir
96+
defmodule MyApp.Migrations.CreateMoviesAndPlays do
97+
use Ecto.Migration
98+
99+
def change do
100+
create table("movies") do
101+
add :title, :string, null: false
102+
add :release_date, :date
103+
timestamps()
104+
end
105+
106+
# The foreign key is in the belongs_to schema
107+
create table("screenplays") do
108+
add :lead_writer, :string, null: false
109+
add :movie_id,
110+
references(:movies, on_delete: :delete_all),
111+
null: false
112+
113+
timestamps()
114+
end
115+
end
116+
end
117+
```
118+
66119
## Many to many
67120
{: .col-2}
68121

@@ -141,6 +194,40 @@ end
141194
```
142195
{: .wrap}
143196

197+
### The migration
198+
199+
It applies to both join tables and schemas.
200+
201+
```elixir
202+
defmodule MyApp.Migrations.CreateUsersAndOrgs do
203+
use Ecto.Migration
204+
205+
def change do
206+
create table("users") do
207+
timestamps()
208+
end
209+
210+
create table("organizations") do
211+
timestamps()
212+
end
213+
214+
create table("users_organizations", primary_key: false) do
215+
add :user_id,
216+
references(:users, on_delete: :delete_all),
217+
null: false
218+
219+
add :organization_id,
220+
references(:organizations, on_delete: :delete_all),
221+
null: false
222+
223+
timestamps()
224+
end
225+
226+
create unique_index(:users_organizations, [:user_id, :organization_id])
227+
end
228+
end
229+
```
230+
144231
## Querying associated records
145232
{: .col-2}
146233

guides/introduction/Embedded Schemas.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22

33
Embedded schemas allow you to define and validate structured data. This data can live in memory, or can be stored in the database. Some use cases for embedded schemas include:
44

5-
- You are maintaining intermediate-state data, like when UI form fields map onto multiple tables in a database.
5+
- You are maintaining intermediate-state data, like when UI form fields map onto multiple tables in a database, or to model entities which are not backed by a database, such as a contact form
66

77
- You are working within a persisted parent schema and you want to embed data that is...
8-
98
- simple, like a map of user preferences inside a User schema.
109
- changes often, like a list of product images with associated structured data inside a Product schema.
1110
- requires complex tracking and validation, like an Address schema inside a User schema.

lib/ecto/schema.ex

+39-15
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,25 @@ defmodule Ecto.Schema do
66
`schema/2` and `embedded_schema/1`.
77
88
`schema/2` is typically used to map data from a persisted source,
9-
usually a database table, into Elixir structs and vice-versa. For
10-
this reason, the first argument of `schema/2` is the source (table)
11-
name. Structs defined with `schema/2` also contain a `__meta__` field
12-
with metadata holding the status of the struct, for example, if it
13-
has been built, loaded or deleted.
9+
usually a database table, into Elixir structs and vice-versa via
10+
the `Ecto.Repo` module. For this reason, the first argument of `schema/2`
11+
is the source (table) name. Structs defined with `schema/2` also contain
12+
a `__meta__` field with metadata holding the status of the struct,
13+
for example, if it has been built, loaded or deleted. Schemas also support
14+
associations, through APIs such as `has_one/3` and `belongs_to/3`.
15+
Check out the [Associations cheatsheet](associations.cheatmd) for a reference
16+
on the different associations types and their migrations.
1417
1518
On the other hand, `embedded_schema/1` is used for defining schemas
1619
that are embedded in other schemas or only exist in-memory. For example,
1720
you can use such schemas to receive data from a command line interface
18-
and validate it, without ever persisting it elsewhere. Such structs
19-
do not contain a `__meta__` field, as they are never persisted.
21+
or a contact form, and validate it, without ever persisting it elsewhere.
22+
Such structs do not contain a `__meta__` field, as they are never persisted.
2023
21-
Besides working as data mappers, `embedded_schema/1` and `schema/2` can
22-
also be used together to decouple how the data is represented in your
23-
applications from the database. Let's see some examples.
24+
Both schemas can be used alongside changesets to filter, cast, and validate
25+
data. Besides working as data mappers, `embedded_schema/1` and `schema/2`
26+
can also be used together to decouple how the data is represented in your
27+
applications from the database.
2428
2529
## Example
2630
@@ -793,6 +797,11 @@ defmodule Ecto.Schema do
793797
[post] = Repo.all(from(p in Post, where: p.id == 42, preload: :comments))
794798
post.comments #=> [%Comment{...}, ...]
795799
800+
If using [EctoSQL](https://hexdocs.pm/ecto_sql), the foreign key should be
801+
defined in the `comments` table, as shown in `belongs_to/3` examples.
802+
You may also see the [Associations cheatsheet](associations.cheatmd)
803+
for more examples.
804+
796805
`has_many` can be used to define hierarchical relationships within a single
797806
schema, for example threaded comments.
798807
@@ -1022,6 +1031,11 @@ defmodule Ecto.Schema do
10221031
# The permalink can come preloaded on the post struct
10231032
[post] = Repo.all(from(p in Post, where: p.id == 42, preload: :permalink))
10241033
post.permalink #=> %Permalink{...}
1034+
1035+
If using [EctoSQL](https://hexdocs.pm/ecto_sql), a foreign key must be defined
1036+
in the `permalinks` and `categories` tables, as shown in `belongs_to/3`
1037+
examples. You may also see the [Associations cheatsheet](associations.cheatmd)
1038+
for more examples.
10251039
"""
10261040
defmacro has_one(name, schema, opts \\ []) do
10271041
schema = expand_literals(schema, __CALLER__)
@@ -1110,6 +1124,16 @@ defmodule Ecto.Schema do
11101124
end
11111125
end
11121126
1127+
If using [EctoSQL](https://hexdocs.pm/ecto_sql), the `comments` table
1128+
should have a `post_id` column that references the `posts` table.
1129+
In your migrations, this can be done as:
1130+
1131+
add :post_id,
1132+
references(:posts, on_delete: :delete_all),
1133+
null: false
1134+
1135+
See the [Associations cheatsheet](associations.cheatmd) for more examples.
1136+
11131137
## Polymorphic associations
11141138
11151139
One common use case for belongs to associations is to handle
@@ -1200,7 +1224,7 @@ defmodule Ecto.Schema do
12001224
12011225
The third and final option is to use `many_to_many/3` to
12021226
define the relationships between the resources. In this case,
1203-
the comments table won't have the foreign key, instead there
1227+
the `comments` table won't have the foreign key, instead there
12041228
is an intermediary table responsible for associating the entries:
12051229
12061230
defmodule Comment do
@@ -1376,17 +1400,17 @@ defmodule Ecto.Schema do
13761400
include any further columns, as those values won't be set by Ecto:
13771401
13781402
create table(:posts_tags, primary_key: false) do
1379-
add :post_id, references(:posts)
1380-
add :tag_id, references(:tags)
1403+
add :post_id, references(:posts, on_delete: :delete_all), null: false
1404+
add :tag_id, references(:tags, on_delete: :delete_all), null: false
13811405
end
13821406
13831407
However, if your `:join_through` is a schema, like `MyApp.PostTag`, your
13841408
join table may be structured as any other table in your codebase,
13851409
including timestamps:
13861410
13871411
create table(:posts_tags) do
1388-
add :post_id, references(:posts)
1389-
add :tag_id, references(:tags)
1412+
add :post_id, references(:posts, on_delete: :delete_all), null: false
1413+
add :tag_id, references(:tags, on_delete: :delete_all), null: false
13901414
timestamps()
13911415
end
13921416

0 commit comments

Comments
 (0)