Skip to content

Latest commit

 

History

History
137 lines (104 loc) · 4.69 KB

EE_自定义表主键.md

File metadata and controls

137 lines (104 loc) · 4.69 KB

自定义主键

有时我们只在一个已经存在的数据库之上来开发我们的应用,我们不能控制数据库的是怎么创建的,而为此修改数据库代价又 比较昂贵。

Ecto 需要一个表中有一个自增的整型字段作为主键。但如果这个现有的数据库是以一个字符串作为主键呢? 没关系,我们可 以使用自定义的主键来创建模型。Ecto 会像我们有整型主键一样正常工作。

注意 : 尽管 Ecto 支持非整型主键,我们最好还是在新项目中使用整型作为主键。

另外,这里选择 name 只是为了演示的简单性, name 并不是主键的最佳选择。

比如我们需要一个 JSON 资源存储一个球队的信息表(每行是一个球员信息),每个球员有一个 名字, 一个 场上位置, 以 及一个 球衣号码, 现有的数据库需要每个表有一个字符串类型的主键 ( The database that will back this resource requires that each table have a string for a primary key.)。

我们可以像这样生成资源。

$ mix phoenix.gen.json Player players name:string position:string number:integer
* creating priv/repo/migrations/20150908003815_create_player.exs
* creating web/models/player.ex
* creating test/models/player_test.exs
* creating web/controllers/player_controller.ex
* creating web/views/player_view.ex
* creating test/controllers/player_controller_test.exs
* creating web/views/changeset_view.ex

Add the resource to your api scope in web/router.ex:

    resources "/players", PlayerController

and then update your repository by running migrations:

    $ mix ecto.migrate

我们首先在路由中只用 api scope 添加资源的路由。

. . .
scope "/api", HelloPhoenix do
  pipe_through :api

  resources "/players", PlayerController
end
. . .

现在我们需要在生成的文件上做一些小改动。

先来看看迁移任务的文件, priv/repo/migrations/20150908003815_create_player.exs。 哦们需要改动两处地方,首先 是传递一个 primary_key: falsetable/2 函数让其不生成主键。然后我们给 name 字段的 add/3 函数传递 primary_key: true 让其成为主键。

defmodule HelloPhoenix.Repo.Migrations.CreatePlayer do
  use Ecto.Migration

  def change do
    create table(:players, primary_key: false) do
      add :name, :string, primary_key: true
      add :position, :string
      add :number, :integer

      timestamps
    end
  end
end

接下来打开 web/models/player.ex, 我们需要添加一行模块属性 @primary_key {:name, :string, []} 来描述我们的主 键是一个字符串。然后我们需要告诉 Phoenix 怎样在路由中获取我们的数据的标识。 我们还需要删除 field :name, :string, 因为 :name 现在已经是主键了。

( 原文: Let's move on to web/models/player.ex next. We'll need to add a module attribute @primary_key {:name, :string, []} describing our primary key as a string. Then we'll need to tell Phoenix how to convert our data structure to an ID that is used in the routes: @derive {Phoenix.Param, key: :name}. We'll also need to remove the field :name, :string line because this is our new primary key. If this seems unusual, recall that the schema doesn't list the id field in models where id is the primary key. )

defmodule HelloPhoenix.Player do
  use HelloPhoenix.Web, :model

  @primary_key {:name, :string, []}
  @derive {Phoenix.Param, key: :name}
  schema "players" do
    field :position, :string
    field :number, :integer

    timestamps
  end
  . . .

最后,让我们在 def render("player.json", %{player: player}) 函数体中删除 id: player.id

defmodule HelloPhoenix.PlayerView do
  use HelloPhoenix.Web, :view

  . . .

  def render("player.json", %{player: player}) do
    %{name: player.name,
      position: player.position,
      number: player.number}
  end
end

完成之后,我们来运行迁移任务。

$mix ecto.migrate

players 表看起来如下:

hello_phoenix_dev=# \d players
                Table "public.players"
   Column    |            Type             | Modifiers
-------------+-----------------------------+-----------
 name        | character varying(255)      | not null
 position    | character varying(255)      |
 number      | integer                     |
 inserted_at | timestamp without time zone | not null
 updated_at  | timestamp without time zone | not null
Indexes:
    "players_pkey" PRIMARY KEY, btree (name)

现在我们有了一个以 name 为主键的模型,可以用 Repo.get!/2 来查询,同时我们也可以在路由中直接使用 -- localhost:4000/players/iguberman