我有以下架构:
schema "countries" do
belongs_to :code, CountryCode, references: :alpha2
belongs_to :language, LanguageCode, references: :code
field :text, :string
timestamps
end
我的问题是,如何为上面的架构编写变更集功能?
我试过了:
def changeset(model, params \\ %{}) do
model
|> cast(params, [:text])
|> cast_assoc(:code)
|> cast_assoc(:language)
|> validate_required([:code, :language, :text])
end
我收到了错误消息:
#Ecto.Changeset<action: nil, changes: %{text: "Switzerland"},
errors: [language: {"is invalid", [type: :map]},
code: {"is invalid", [type: :map]}], data: #Busiket.Country<>, valid?: false>
更新
我重写了变更集功能:
def changeset(model, params \\ %{}) do
model
|> cast(params, [:code_id, :language_id, :text])
|> cast_assoc(:code)
|> cast_assoc(:language)
|> validate_required([:code, :language, :text])
end
我得到了:
#Ecto.Changeset<action: nil, changes: %{text: "Switzerland"},
errors: [language: {"is invalid", [type: :map]},
code: {"is invalid", [type: :map]}], data: #Busiket.Country<>, valid?: false>
很抱歉,这是我LanguageCode
的架构:
schema "languages_code" do
has_one :code, Country, foreign_key: :lang
field :text, :string
timestamps
end
更新
我在shell中再次测试它:
iex(4)> v = %{code: %{code: "CH"}, language: %{alpha2: "DE"}, text: "Schweiz"}
%{code: %{code: "CH"}, language: %{alpha2: "DE"}, text: "Schweiz"}
iex(5)> c = Country.changeset(%Country{}, v)
#Ecto.Changeset<action: nil,
changes: %{code: #Ecto.Changeset<action: :insert, changes: %{},
errors: [alpha2: {"can't be blank", []}, alpha3: {"can't be blank", []}],
data: #Busiket.CountryCode<>, valid?: false>,
language: #Ecto.Changeset<action: :insert, changes: %{},
errors: [code: {"can't be blank", []}, text: {"can't be blank", []}],
data: #Busiket.LanguageCode<>, valid?: false>, text: "Schweiz"}, errors: [],
data: #Busiket.Country<>, valid?: false>
我忘了提及,language_code表上的数据已经可用:
答案 0 :(得分:0)
正在生成的变更集正在尝试插入3条新记录,Country,LanguageCode和CountryCode。
您的参数中没有足够的数据来插入所有这些记录:
%{code: %{code: "CH"}, language: %{alpha2: "DE"}, text: "Schweiz"}
如果没有alpha2
和alpha3
字段,则无法插入CountryCode表:
code: #Ecto.Changeset<action: :insert,
changes: %{},
errors: [alpha2: {"can't be blank", []}, alpha3: {"can't be blank", []}],
data: #Busiket.CountryCode<>,
valid?: false>`
如果没有code
和text
字段,则无法插入到LanguageCode表中:
language: #Ecto.Changeset<action: :insert,
changes: %{},
errors: [code: {"can't be blank", []}, text: {"can't be blank", []}],
data: #Busiket.LanguageCode<>,
valid?: false>
如果您不打算插入所有这些表格,那么您需要填充language_id
和code_id
参数。
params = %{language_id: "DE", code_id: "CH", text: "Schweiz"}
然后从变更集功能中删除cast_assoc
次调用:
def changeset(model, params \\ %{}) do
model
|> cast(params, [:code_id, :language_id, :text])
|> validate_required([:code_id, :language_id, :text])
end
或者,您可以预加载关联并使用put_assoc
:
def changeset(
model = %Country{},
country_code = %CountryCode{},
language_code = %LanguageCode{},
params \\ %{}) do
model
|> cast(params, [:text])
|> put_assoc(:code, country_code)
|> put_assoc(:language, language_code)
|> validate_required([:text, :code, :language])
end
cast_assoc
的{{3}}对cast_assoc
和put_assoc
之间的差异有一些额外的解释:
换句话说,当关联数据与父结构一起管理时,cast_assoc / 3非常有用。如果关联的每一侧都是单独管理的,最好使用put_assoc / 3并直接指示Ecto关联的外观。