Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to customize the primary key name #5766

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/mix/tasks/phx.gen.context.ex
Expand Up @@ -89,6 +89,7 @@ defmodule Mix.Tasks.Phx.Gen.Context do
merge_with_existing_context: :boolean,
prefix: :string,
live: :boolean,
primary_key: :string,
compile: :boolean
]

Expand Down
10 changes: 9 additions & 1 deletion lib/mix/tasks/phx.gen.schema.ex
Expand Up @@ -86,6 +86,13 @@ defmodule Mix.Tasks.Phx.Gen.Schema do
Generated migration can use `binary_id` for schema's primary key
and its references with option `--binary-id`.

## primary_key

By default, the primary key in the table is called `id`. This option
allows to change the name of the primary key column. For example:

$ mix phx.gen.schema Blog.post posts --primary-key post_id

## repo

Generated migration can use `repo` to set the migration repository
Expand Down Expand Up @@ -146,7 +153,8 @@ defmodule Mix.Tasks.Phx.Gen.Schema do
alias Mix.Phoenix.Schema

@switches [migration: :boolean, binary_id: :boolean, table: :string, web: :string,
context_app: :string, prefix: :string, repo: :string, migration_dir: :string]
context_app: :string, prefix: :string, repo: :string, migration_dir: :string,
primary_key: :string]

@doc false
def run(args) do
Expand Down
6 changes: 3 additions & 3 deletions priv/templates/phx.gen.context/test_cases.exs
Expand Up @@ -13,7 +13,7 @@

test "get_<%= schema.singular %>!/1 returns the <%= schema.singular %> with given id" do
<%= schema.singular %> = <%= schema.singular %>_fixture()
assert <%= inspect context.alias %>.get_<%= schema.singular %>!(<%= schema.singular %>.id) == <%= schema.singular %>
assert <%= inspect context.alias %>.get_<%= schema.singular %>!(<%= schema.singular %>.<%= schema.opts[:primary_key] || :id %>) == <%= schema.singular %>
end

test "create_<%= schema.singular %>/1 with valid data creates a <%= schema.singular %>" do
Expand All @@ -38,13 +38,13 @@
test "update_<%= schema.singular %>/2 with invalid data returns error changeset" do
<%= schema.singular %> = <%= schema.singular %>_fixture()
assert {:error, %Ecto.Changeset{}} = <%= inspect context.alias %>.update_<%= schema.singular %>(<%= schema.singular %>, @invalid_attrs)
assert <%= schema.singular %> == <%= inspect context.alias %>.get_<%= schema.singular %>!(<%= schema.singular %>.id)
assert <%= schema.singular %> == <%= inspect context.alias %>.get_<%= schema.singular %>!(<%= schema.singular %>.<%= schema.opts[:primary_key] || :id %>)
end

test "delete_<%= schema.singular %>/1 deletes the <%= schema.singular %>" do
<%= schema.singular %> = <%= schema.singular %>_fixture()
assert {:ok, %<%= inspect schema.alias %>{}} = <%= inspect context.alias %>.delete_<%= schema.singular %>(<%= schema.singular %>)
assert_raise Ecto.NoResultsError, fn -> <%= inspect context.alias %>.get_<%= schema.singular %>!(<%= schema.singular %>.id) end
assert_raise Ecto.NoResultsError, fn -> <%= inspect context.alias %>.get_<%= schema.singular %>!(<%= schema.singular %>.<%= schema.opts[:primary_key] || :id %>) end
end

test "change_<%= schema.singular %>/1 returns a <%= schema.singular %> changeset" do
Expand Down
7 changes: 6 additions & 1 deletion priv/templates/phx.gen.live/index.ex
Expand Up @@ -5,8 +5,13 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
alias <%= inspect schema.module %>

@impl true
def mount(_params, _session, socket) do
def mount(_params, _session, socket) do<%= if schema.opts[:primary_key] do %>
{:ok,
socket
|> stream_configure(:<%= schema.collection %>, dom_id: &"<%= schema.table %>-#{&1.<%= schema.opts[:primary_key] %>}")
|> stream(:<%= schema.collection %>, <%= inspect context.alias %>.list_<%= schema.plural %>())}<% else %>
{:ok, stream(socket, :<%= schema.collection %>, <%= inspect context.alias %>.list_<%= schema.plural %>())}
<% end %>
end

@impl true
Expand Down
4 changes: 2 additions & 2 deletions priv/templates/phx.gen.live/index.html.heex
Expand Up @@ -21,7 +21,7 @@
</:action>
<:action :let={{id, <%= schema.singular %>}}>
<.link
phx-click={JS.push("delete", value: %{id: <%= schema.singular %>.id}) |> hide("##{id}")}
phx-click={JS.push("delete", value: %{id: <%= schema.singular %>.<%= schema.opts[:primary_key] || :id %>}) |> hide("##{id}")}
data-confirm="Are you sure?"
>
Delete
Expand All @@ -32,7 +32,7 @@
<.modal :if={@live_action in [:new, :edit]} id="<%= schema.singular %>-modal" show on_cancel={JS.patch(~p"<%= schema.route_prefix %>")}>
<.live_component
module={<%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent}
id={@<%= schema.singular %>.id || :new}
id={@<%= schema.singular %>.<%= schema.opts[:primary_key] || :id %> || :new}
title={@page_title}
action={@live_action}
<%= schema.singular %>={@<%= schema.singular %>}
Expand Down
6 changes: 3 additions & 3 deletions priv/templates/phx.gen.live/live_test.exs
Expand Up @@ -49,7 +49,7 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
test "updates <%= schema.singular %> in listing", %{conn: conn, <%= schema.singular %>: <%= schema.singular %>} do
{:ok, index_live, _html} = live(conn, ~p"<%= schema.route_prefix %>")

assert index_live |> element("#<%= schema.plural %>-#{<%= schema.singular %>.id} a", "Edit") |> render_click() =~
assert index_live |> element("#<%= schema.plural %>-#{<%= schema.singular %>.<%= schema.opts[:primary_key] || :id %>} a", "Edit") |> render_click() =~
"Edit <%= schema.human_singular %>"

assert_patch(index_live, ~p"<%= schema.route_prefix %>/#{<%= schema.singular %>}/edit")
Expand All @@ -72,8 +72,8 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
test "deletes <%= schema.singular %> in listing", %{conn: conn, <%= schema.singular %>: <%= schema.singular %>} do
{:ok, index_live, _html} = live(conn, ~p"<%= schema.route_prefix %>")

assert index_live |> element("#<%= schema.plural %>-#{<%= schema.singular %>.id} a", "Delete") |> render_click()
refute has_element?(index_live, "#<%= schema.plural %>-#{<%= schema.singular %>.id}")
assert index_live |> element("#<%= schema.plural %>-#{<%= schema.singular %>.<%= schema.opts[:primary_key] || :id %>} a", "Delete") |> render_click()
refute has_element?(index_live, "#<%= schema.plural %>-#{<%= schema.singular %>.<%= schema.opts[:primary_key] || :id %>}")
end
end

Expand Down
4 changes: 2 additions & 2 deletions priv/templates/phx.gen.live/show.html.heex
@@ -1,5 +1,5 @@
<.header>
<%= schema.human_singular %> <%%= @<%= schema.singular %>.id %>
<%= schema.human_singular %> <%%= @<%= schema.singular %>.<%= schema.opts[:primary_key] || :id %> %>
<:subtitle>This is a <%= schema.singular %> record from your database.</:subtitle>
<:actions>
<.link patch={~p"<%= schema.route_prefix %>/#{@<%= schema.singular %>}/show/edit"} phx-click={JS.push_focus()}>
Expand All @@ -17,7 +17,7 @@
<.modal :if={@live_action == :edit} id="<%= schema.singular %>-modal" show on_cancel={JS.patch(~p"<%= schema.route_prefix %>/#{@<%= schema.singular %>}")}>
<.live_component
module={<%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent}
id={@<%= schema.singular %>.id}
id={@<%= schema.singular %>.<%= schema.opts[:primary_key] || :id %>}
title={@page_title}
action={@live_action}
<%= schema.singular %>={@<%= schema.singular %>}
Expand Down
2 changes: 1 addition & 1 deletion priv/templates/phx.gen.schema/migration.exs
Expand Up @@ -3,7 +3,7 @@ defmodule <%= inspect schema.repo %>.Migrations.Create<%= Macro.camelize(schema.

def change do
create table(:<%= schema.table %><%= if schema.binary_id do %>, primary_key: false<% end %><%= if schema.prefix do %>, prefix: :<%= schema.prefix %><% end %>) do
<%= if schema.binary_id do %> add :id, :binary_id, primary_key: true
<%= if schema.binary_id do %> add :<%= schema.opts[:primary_key] || :id %>, :binary_id, primary_key: true
<% end %><%= for {k, v} <- schema.attrs do %> add <%= inspect k %>, <%= inspect Mix.Phoenix.Schema.type_for_migration(v) %><%= schema.migration_defaults[k] %>
<% end %><%= for {_, i, _, s} <- schema.assocs do %> add <%= inspect(i) %>, references(<%= inspect(s) %>, on_delete: :nothing<%= if schema.binary_id do %>, type: :binary_id<% end %>)
<% end %>
Expand Down
5 changes: 3 additions & 2 deletions priv/templates/phx.gen.schema/schema.ex
Expand Up @@ -2,8 +2,9 @@ defmodule <%= inspect schema.module %> do
use Ecto.Schema
import Ecto.Changeset
<%= if schema.prefix do %>
@schema_prefix :<%= schema.prefix %><% end %><%= if schema.binary_id do %>
@primary_key {:id, :binary_id, autogenerate: true}
@schema_prefix :<%= schema.prefix %><% end %><%= if schema.opts[:primary_key] do %>
@derive {Phoenix.Param, key: :<%= schema.opts[:primary_key] %>}<% end %><%= if schema.binary_id do %>
@primary_key {:<%= schema.opts[:primary_key] || :id %>, :binary_id, autogenerate: true}
@foreign_key_type :binary_id<% end %>
schema <%= inspect schema.table %> do
<%= Mix.Phoenix.Schema.format_fields_for_schema(schema) %>
Expand Down
19 changes: 19 additions & 0 deletions test/mix/tasks/phx.gen.schema_test.exs
Expand Up @@ -303,6 +303,25 @@ defmodule Mix.Tasks.Phx.Gen.SchemaTest do
end
end

test "generates migration with custom primary key", config do
in_tmp_project config.test, fn ->
Gen.Schema.run(~w(Blog.Post posts title user_id:references:users --binary-id --primary-key post_id))

assert_file "lib/phoenix/blog/post.ex", fn file ->
assert file =~ "@derive {Phoenix.Param, key: :post_id}"
assert file =~ "@primary_key {:post_id, :binary_id, autogenerate: true}"
assert file =~ "field :user_id, :binary_id"
end

assert [migration] = Path.wildcard("priv/repo/migrations/*_create_posts.exs")
assert_file migration, fn file ->
assert file =~ "create table(:posts, primary_key: false) do"
assert file =~ "add :post_id, :binary_id, primary_key: true"
assert file =~ "add :user_id, references(:users, on_delete: :nothing, type: :binary_id)"
end
end
end

test "generates schema and migration with prefix", config do
in_tmp_project config.test, fn ->
Gen.Schema.run(~w(Blog.Post posts title --prefix cms))
Expand Down