使用模式之间的构建关联
Use build association between schemas
我有以下架构:
第一个:
schema "countries_codes" do
# Country code based on ISO-2
field :iso, :string
field :name, :string
has_many :country, Country
timestamps
end
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:iso, :name])
|> validate_required([:iso, :name])
|> validate_length(:iso, max: 2)
end
第二个:
schema "languages_codes" do
# Language code based on ISO-2
field :iso, :string
field :name, :string
has_many :country, Country
timestamps
end
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:iso, :name])
|> validate_required([:iso, :name])
|> validate_length(:iso, max: 2)
end
第三个:
schema "countries" do
belongs_to :country_iso, CountryCode
belongs_to :language_iso, LanguageCode
field :name, :string
timestamps
end
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:country_iso, :language_iso, :name])
|> cast_assoc(:country_iso)
|> validate_required([:country_iso, :language_iso, :name])
end
如您在第三个 table 中所见,第一个和第二个字段属于其他架构。
当我 运行 country
变更集函数时,我得到:
** (RuntimeError) casting assocs with cast/3 is not supported, use cast_assoc/3 instead
(ecto) lib/ecto/changeset.ex:440: Ecto.Changeset.type!/2
(ecto) lib/ecto/changeset.ex:415: Ecto.Changeset.process_param/8
(elixir) lib/enum.ex:1247: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3
(ecto) lib/ecto/changeset.ex:391: Ecto.Changeset.do_cast/7
(busiket) web/models/country.ex:20: Busiket.Country.changeset/2
(elixir) lib/enum.ex:1184: Enum."-map/2-lists^map/1-0-"/2
priv/repo/seeds.exs:48: (file)
(elixir) lib/code.ex:363: Code.require_file/2
(mix) lib/mix/tasks/run.ex:71: Mix.Tasks.Run.run/1
(mix) lib/mix/task.ex:296: Mix.Task.run_task/3
(mix) lib/mix/cli.ex:58: Mix.CLI.run_task/2
(elixir) lib/code.ex:363: Code.require_file/2
我的问题是,我不知道如何为模式编写变更集函数 countries
。
要修复错误,请在架构 countries
.
中删除 :country_iso
和 :language_iso
表单 cast/3
更新模型 Country
:
defmodule MyApp.Country do
use Ecto.Schema
import Ecto.Changeset
schema "countries" do
field :name, :string
belongs_to :country_iso, MyApp.CountryCode
belongs_to :language_iso, MyApp.LanguageCode
timestamps()
end
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:name, :country_iso_id, :language_iso_id])
|> cast_assoc(:country_iso)
|> cast_assoc(:language_iso)
|> unique_constraint(:country_iso_id)
|> unique_constraint(:language_iso_id)
|> validate_required([:name])
end
end
用新的 CountryCode
和 LanguageCode
创建一个新的 Country
:
changeset = Country.changeset(%Country{}, %{
name: "United Kingdom",
country_iso: %{iso: "GB", name: "United Kingdom"},
language_iso: %{iso: "en", name: "English"}
})
Repo.insert! changeset
无法插入具有重复关联的变更集
Repo.insert! %CountryCode{iso: "GB", name: "United Kingdom"}
changeset = Country.changeset(%Country{}, %{
name: "United Kingdom",
country_iso: %{iso: "GB", name: "United Kingdom 2"},
language_iso: %{iso: "en", name: "English"}
})
{:error, changeset} = Repo.insert changeset
changeset.valid? # => false
changeset.changes[:country_iso].errors[:iso] # => {"has already been taken", []}
需要将 unique_constraint(:iso)
添加到 CountryCode
变更集中以使其像那样工作。
插入具有现有关联的变更集
country_iso = Repo.insert! %CountryCode{iso: "GB", name: "United Kingdom"}
changeset = Country.changeset(%Country{}, %{
name: "United Kingdom",
country_iso_id: country_iso.id,
language_iso: %{iso: "en", name: "English"}
})
Repo.insert! changeset
country = Repo.one Country
country.country_iso_id == country_iso.id # => true
考虑使用列 iso
作为表 country_codes
和 language_codes
的主键
查看更多 - Ecto Custom Primary Keys
迁移:
defmodule MyApp.Repo.Migrations.CreateCountryCode do
use Ecto.Migration
def change do
create table(:countries_codes, primary_key: false) do
add :iso, :string, size: 2, primary_key: true
add :name, :string
timestamps()
end
end
end
defmodule MyApp.Repo.Migrations.CreateLanguageCode do
use Ecto.Migration
def change do
create table(:languages_codes, primary_key: false) do
add :iso, :string, size: 2, primary_key: true
add :name, :string
timestamps()
end
end
end
defmodule MyApp.Repo.Migrations.CreateCountry do
use Ecto.Migration
def change do
create table(:countries) do
add :name, :string
add :country_iso, references(:countries_codes, column: :iso, type: :string, on_delete: :nothing)
add :language_iso, references(:languages_codes, column: :iso, type: :string, on_delete: :nothing)
timestamps()
end
create unique_index(:countries, [:country_iso, :language_iso])
end
end
型号:
defmodule MyApp.CountryCode do
use Ecto.Schema
import Ecto.Changeset
@primary_key {:iso, :string, []}
schema "countries_codes" do
field :name, :string
has_many :countries, MyApp.Country
timestamps()
end
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:iso, :name])
|> validate_required([:iso, :name])
|> validate_length(:iso, max: 2)
|> unique_constraint(:iso)
end
end
defmodule MyApp.LanguageCode do
use Ecto.Schema
import Ecto.Changeset
@primary_key {:iso, :string, []}
schema "languages_codes" do
field :name, :string
has_many :countries, MyApp.Country
timestamps()
end
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:iso, :name])
|> validate_required([:iso, :name])
|> validate_length(:iso, max: 2)
|> unique_constraint(:iso)
end
end
defmodule MyApp.Country do
use Ecto.Schema
import Ecto.Changeset
schema "countries" do
field :name, :string
belongs_to :country_code, MyApp.CountryCode, foreign_key: :country_iso, references: :iso, type: :string
belongs_to :language_code, MyApp.LanguageCode, foreign_key: :language_iso, references: :iso, type: :string
timestamps()
end
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:name, :country_iso, :language_iso])
|> validate_required([:name, :country_iso, :language_iso])
|> foreign_key_constraint(:country_iso)
|> foreign_key_constraint(:language_iso)
end
end
注意 foreign_key_constraint/3 函数 - 当使用不存在的 country_iso
或 language_iso
时,它会为 Country
变更集添加验证错误。
我还将关联名称更改为 country_code
和 language_code
假设有一些国家代码和语言代码:
Repo.insert! %CountryCode{iso: "GB", name: "United Kingdom"}
Repo.insert! %LanguageCode{iso: "en", name: "English"}
创建国家变更集并插入数据库:
changeset = Country.changeset(%Country{}, %{name: "United Kingdom", country_iso: "GB", language_iso: "en"})
Repo.insert! changeset
我有以下架构:
第一个:
schema "countries_codes" do
# Country code based on ISO-2
field :iso, :string
field :name, :string
has_many :country, Country
timestamps
end
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:iso, :name])
|> validate_required([:iso, :name])
|> validate_length(:iso, max: 2)
end
第二个:
schema "languages_codes" do
# Language code based on ISO-2
field :iso, :string
field :name, :string
has_many :country, Country
timestamps
end
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:iso, :name])
|> validate_required([:iso, :name])
|> validate_length(:iso, max: 2)
end
第三个:
schema "countries" do
belongs_to :country_iso, CountryCode
belongs_to :language_iso, LanguageCode
field :name, :string
timestamps
end
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:country_iso, :language_iso, :name])
|> cast_assoc(:country_iso)
|> validate_required([:country_iso, :language_iso, :name])
end
如您在第三个 table 中所见,第一个和第二个字段属于其他架构。
当我 运行 country
变更集函数时,我得到:
** (RuntimeError) casting assocs with cast/3 is not supported, use cast_assoc/3 instead
(ecto) lib/ecto/changeset.ex:440: Ecto.Changeset.type!/2
(ecto) lib/ecto/changeset.ex:415: Ecto.Changeset.process_param/8
(elixir) lib/enum.ex:1247: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3
(ecto) lib/ecto/changeset.ex:391: Ecto.Changeset.do_cast/7
(busiket) web/models/country.ex:20: Busiket.Country.changeset/2
(elixir) lib/enum.ex:1184: Enum."-map/2-lists^map/1-0-"/2
priv/repo/seeds.exs:48: (file)
(elixir) lib/code.ex:363: Code.require_file/2
(mix) lib/mix/tasks/run.ex:71: Mix.Tasks.Run.run/1
(mix) lib/mix/task.ex:296: Mix.Task.run_task/3
(mix) lib/mix/cli.ex:58: Mix.CLI.run_task/2
(elixir) lib/code.ex:363: Code.require_file/2
我的问题是,我不知道如何为模式编写变更集函数 countries
。
要修复错误,请在架构 countries
.
:country_iso
和 :language_iso
表单 cast/3
更新模型 Country
:
defmodule MyApp.Country do
use Ecto.Schema
import Ecto.Changeset
schema "countries" do
field :name, :string
belongs_to :country_iso, MyApp.CountryCode
belongs_to :language_iso, MyApp.LanguageCode
timestamps()
end
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:name, :country_iso_id, :language_iso_id])
|> cast_assoc(:country_iso)
|> cast_assoc(:language_iso)
|> unique_constraint(:country_iso_id)
|> unique_constraint(:language_iso_id)
|> validate_required([:name])
end
end
用新的 CountryCode
和 LanguageCode
创建一个新的 Country
:
changeset = Country.changeset(%Country{}, %{
name: "United Kingdom",
country_iso: %{iso: "GB", name: "United Kingdom"},
language_iso: %{iso: "en", name: "English"}
})
Repo.insert! changeset
无法插入具有重复关联的变更集
Repo.insert! %CountryCode{iso: "GB", name: "United Kingdom"}
changeset = Country.changeset(%Country{}, %{
name: "United Kingdom",
country_iso: %{iso: "GB", name: "United Kingdom 2"},
language_iso: %{iso: "en", name: "English"}
})
{:error, changeset} = Repo.insert changeset
changeset.valid? # => false
changeset.changes[:country_iso].errors[:iso] # => {"has already been taken", []}
需要将 unique_constraint(:iso)
添加到 CountryCode
变更集中以使其像那样工作。
插入具有现有关联的变更集
country_iso = Repo.insert! %CountryCode{iso: "GB", name: "United Kingdom"}
changeset = Country.changeset(%Country{}, %{
name: "United Kingdom",
country_iso_id: country_iso.id,
language_iso: %{iso: "en", name: "English"}
})
Repo.insert! changeset
country = Repo.one Country
country.country_iso_id == country_iso.id # => true
考虑使用列 iso
作为表 country_codes
和 language_codes
的主键
查看更多 - Ecto Custom Primary Keys
迁移:
defmodule MyApp.Repo.Migrations.CreateCountryCode do
use Ecto.Migration
def change do
create table(:countries_codes, primary_key: false) do
add :iso, :string, size: 2, primary_key: true
add :name, :string
timestamps()
end
end
end
defmodule MyApp.Repo.Migrations.CreateLanguageCode do
use Ecto.Migration
def change do
create table(:languages_codes, primary_key: false) do
add :iso, :string, size: 2, primary_key: true
add :name, :string
timestamps()
end
end
end
defmodule MyApp.Repo.Migrations.CreateCountry do
use Ecto.Migration
def change do
create table(:countries) do
add :name, :string
add :country_iso, references(:countries_codes, column: :iso, type: :string, on_delete: :nothing)
add :language_iso, references(:languages_codes, column: :iso, type: :string, on_delete: :nothing)
timestamps()
end
create unique_index(:countries, [:country_iso, :language_iso])
end
end
型号:
defmodule MyApp.CountryCode do
use Ecto.Schema
import Ecto.Changeset
@primary_key {:iso, :string, []}
schema "countries_codes" do
field :name, :string
has_many :countries, MyApp.Country
timestamps()
end
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:iso, :name])
|> validate_required([:iso, :name])
|> validate_length(:iso, max: 2)
|> unique_constraint(:iso)
end
end
defmodule MyApp.LanguageCode do
use Ecto.Schema
import Ecto.Changeset
@primary_key {:iso, :string, []}
schema "languages_codes" do
field :name, :string
has_many :countries, MyApp.Country
timestamps()
end
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:iso, :name])
|> validate_required([:iso, :name])
|> validate_length(:iso, max: 2)
|> unique_constraint(:iso)
end
end
defmodule MyApp.Country do
use Ecto.Schema
import Ecto.Changeset
schema "countries" do
field :name, :string
belongs_to :country_code, MyApp.CountryCode, foreign_key: :country_iso, references: :iso, type: :string
belongs_to :language_code, MyApp.LanguageCode, foreign_key: :language_iso, references: :iso, type: :string
timestamps()
end
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:name, :country_iso, :language_iso])
|> validate_required([:name, :country_iso, :language_iso])
|> foreign_key_constraint(:country_iso)
|> foreign_key_constraint(:language_iso)
end
end
注意 foreign_key_constraint/3 函数 - 当使用不存在的 country_iso
或 language_iso
时,它会为 Country
变更集添加验证错误。
我还将关联名称更改为 country_code
和 language_code
假设有一些国家代码和语言代码:
Repo.insert! %CountryCode{iso: "GB", name: "United Kingdom"}
Repo.insert! %LanguageCode{iso: "en", name: "English"}
创建国家变更集并插入数据库:
changeset = Country.changeset(%Country{}, %{name: "United Kingdom", country_iso: "GB", language_iso: "en"})
Repo.insert! changeset