将JSON数据更新到postgresql db的问题:**(Ecto.CastError)期望参数是一个:map,got



嗨,我对Elixir/Phoenix非常陌生。我想做的很简单。我的数据库中有一个带有 jsonb 列的表。我正在尝试通过 Ecto 更新 json 数据。我的模型如下所示:

defmodule Setting.Setting do
use Setting.Web, :model
alias Setting.{VesselState, User, SettingData, FollowUp, LogEntry, VesselEvent, RiskModel, LegalExtract}
schema "settings" do
belongs_to :vessel_state, VesselState
field :request_date, :date
belongs_to :requester, User
field :approved_date, :date
belongs_to :manager, User
field :analyst_feedback, :string
belongs_to :analyst, User
field :date, :utc_datetime
field :status, :string
field :points, :integer
field :risk, :string
field :verdict, :string
embeds_one :data, SettingData, on_replace: :delete
field :remarks, {:array, :string}
embeds_many :follow_ups, FollowUp, on_replace: :delete
embeds_many :log, LogEntry
field :uuid, :string
has_one :vessel_event, VesselEvent
belongs_to :risk_model, RiskModel
has_many :legal_extracts, LegalExtract
timestamps()
end

架构中的data列是 json,你会看到它正在嵌入另一个模型 (SettingData)。如果我从Setting表中获取一条记录,它将如下所示:

%Setting.Setting{
__meta__: #Ecto.Schema.Metadata<:loaded, "settings">,
analyst: #Ecto.Association.NotLoaded<association :analyst is not loaded>,
analyst_feedback: "Approved",
analyst_id: nil,
approved_date: nil,
data: %Setting.SettingData{
id: "f25b6080-0751-4a65-8214-36e2810ea716",
v1: %Setting.SettingDataV1{
age_risk: %Setting.AgeRisk{
age: 5.15,
cif: false,
id: "70b249f6-cef9-4473-a0ed-780e84cf6407",
},
class_society_risk: %Setting.ClassSocietyRisk{
class_society: "Lloyds Register",
iacs_member: true,
id: "248bc975-5c39-4f92-afb6-a2b2365afa53",
original_points: nil,
overdue_class_published: false,
}
},
date: ~U[2016-12-16 00:00:00.000000Z],
follow_ups: [
%Setting.FollowUp{
action: nil,
attachments: [],
comment: nil,
created_at: nil,
file_reference: nil,
id: "314b4445-b162-49f3-a1ca-f39a24af14bb",
}
],
id: 8,
inserted_at: ~U[2017-01-02 08:25:58.913674Z],
legal_extracts: #Ecto.Association.NotLoaded<association :legal_extracts is not loaded>,
log: [],
manager: #Ecto.Association.NotLoaded<association :manager is not loaded>,
manager_id: nil,
points: 15,
remarks: [""],
request_date: nil,
requester: #Ecto.Association.NotLoaded<association :requester is not loaded>,
requester_id: nil,
risk: "Standard",
risk_model: #Ecto.Association.NotLoaded<association :risk_model is not loaded>,
risk_model_id: nil,
status: "Completed",
updated_at: ~U[2017-02-01 15:27:53.158000Z],
uuid: nil,
verdict: "Approved",
vessel_event: #Ecto.Association.NotLoaded<association :vessel_event is not loaded>,
vessel_state: #Ecto.Association.NotLoaded<association :vessel_state is not loaded>,
vessel_state_id: 8
}

我只是想data.v1.age_risk.ciffalse更改为true.我最初试图创建一个像这样的change_set,

cs = %{
data: %{
v1: %{
age_risk: %{
cif: true
}
}
}
}
Setting.Setting.changeset(setting, cs) |> Setting.Repo.update()

这会将cif更新为truefromfalse,但它会将 json 数据中的所有其他元素设置为nil。所以我认为 Ill 必须获取整个 json,然后更新我想要的元素,然后使用新的 json 更新数据库。所以基本上我做了这样的事情:

cs_new = %Setting.Setting{
__meta__: #Ecto.Schema.Metadata<:loaded, "settings">,
analyst: #Ecto.Association.NotLoaded<association :analyst is not loaded>,
analyst_feedback: "Approved",
analyst_id: nil,
approved_date: nil,
data: %Setting.SettingData{
id: "f25b6080-0751-4a65-8214-36e2810ea716",
v1: %Setting.SettingDataV1{
age_risk: %Setting.AgeRisk{
age: 5.15,
cif: true,     #changed this to true
id: "70b249f6-cef9-4473-a0ed-780e84cf6407",
},
class_society_risk: %Setting.ClassSocietyRisk{
class_society: "Lloyds Register",
iacs_member: true,
id: "248bc975-5c39-4f92-afb6-a2b2365afa53",
original_points: nil,
overdue_class_published: false,
}
},
date: ~U[2016-12-16 00:00:00.000000Z],
follow_ups: [
%Setting.FollowUp{
action: nil,
attachments: [],
comment: nil,
created_at: nil,
file_reference: nil,
id: "314b4445-b162-49f3-a1ca-f39a24af14bb",
}
],
id: 8,
inserted_at: ~U[2017-01-02 08:25:58.913674Z],
legal_extracts: #Ecto.Association.NotLoaded<association :legal_extracts is not loaded>,
log: [],
manager: #Ecto.Association.NotLoaded<association :manager is not loaded>,
manager_id: nil,
points: 15,
remarks: [""],
request_date: nil,
requester: #Ecto.Association.NotLoaded<association :requester is not loaded>,
requester_id: nil,
risk: "Standard",
risk_model: #Ecto.Association.NotLoaded<association :risk_model is not loaded>,
risk_model_id: nil,
status: "Completed",
updated_at: ~U[2017-02-01 15:27:53.158000Z],
uuid: nil,
verdict: "Approved",
vessel_event: #Ecto.Association.NotLoaded<association :vessel_event is not loaded>,
vessel_state: #Ecto.Association.NotLoaded<association :vessel_state is not loaded>,
vessel_state_id: 8
}
Setting.Setting.changeset(setting, cs_new) |> Setting.Repo.update()

这现在给了我错误:

** (Ecto.CastError) expected params to be a :map, got: `%Setting.Setting ...

我尝试使用Map.from_struct/1将其转换为map,但这只会更改外层。我应该如何解决这个问题?还是有不同(且更好)的方式来实现 IM 想要做的事情?

你在Ecto.Changeset.cast_assoc/3之后

。您应该为data定义一个变更集,并允许 ecto 为您完成其余工作,而不是直接更新值。


有点像这样:

Ecto.Changeset.cast_assoc(
setting, :data, with: &SettingData.changeset/2
)

最新更新