ecto 变更集为现有添加关联

Posted

技术标签:

【中文标题】ecto 变更集为现有添加关联【英文标题】:ecto changeset add assoc for existing 【发布时间】:2020-02-15 16:48:15 【问题描述】:

早上好!我已经到了挣扎的时刻。总而言之,我正在尝试创建一个模式/模型,该模式/模型具有关联的模式/模型已经存在的关联。我发现的每个示例看起来都更像 Rails 嵌套属性。这是我的各种代码:

带有关联的架构(及其变更集函数):

schema "payor_procedure_mappings" do
  belongs_to :payor, Payor
  belongs_to :procedure, Procedure
  field :payor_procedure_name, :string
  field :payor_procedure_id, :string

  timestamps()
end

def changeset(model, params \\ %) do 
  model
  |> cast(params, all_params())
  |> validate_required(required())
  |> cast_assoc(:payor, params[:payor])  # <--- LINE 50 from stacktrace
end

defp required do
  :payor_procedure_name,
  :payor_procedure_id
]

我想与之关联的预先存在的架构:

schema "payors" do
  field :name, :string, null: :false
  field :polling_url, :string, null: :false

  timestamps()
end

def changeset(model, params \\ %) do 
  model
  |> cast(params, all_params())
  |> validate_required(required())
end 

这是我尝试设置关联的代码:

test "valid if all required params are present", %procedure: procedure, payor: payor do 
  params = %
    payor: payor,
    payor_procedure_name: "That one thing we do to patients",
    payor_procedure_id: "2342"
  

changeset = 
  %PayorProcedureMapping
  |> PayorProcedureMapping.changeset(params)   # THIS IS LINE 28 from stacktrace

  assert changeset.valid?
end

(请注意,测试函数的第二个参数中的付款人是已经加载的架构/模型结构)

要清楚,我传入的参数如下所示:

%
  payor: %ReimbursementMonitor.Schemas.Payor
    __meta__: #Ecto.Schema.Metadata<:loaded, "payors">,
    id: 12,
    inserted_at: ~N[2020-02-15 16:30:16],
    name: "test_name",
    polling_url: "www.example.com",
    updated_at: ~N[2020-02-15 16:30:16]
  ,
  payor_procedure_id: "2342",
  payor_procedure_name: "That one thing we do to patients"

我遇到的情况是,如果付款人已经存在,我的测试真的很不爽。尝试运行该测试时出现此错误:

** (FunctionClauseError) no function clause matching in Keyword.get_lazy/3

 The following arguments were given to Keyword.get_lazy/3:

     # 1
     nil

     # 2
     :with

     # 3
     #Function<5.89690842/0 in Ecto.Changeset.cast_relation/4>

 Attempted function clauses (showing 1 out of 1):

def get_lazy(keywords, key, fun) when is_list(keywords) and is_atom(key) and is_function(fun, 0)
  code: |> PayorProcedureMapping.changeset(params)
  stacktrace:
    (elixir) lib/keyword.ex:223: Keyword.get_lazy/3
    (ecto) lib/ecto/changeset.ex:778: Ecto.Changeset.cast_relation/4
    (reimbursement_monitor) lib/reimbursement_monitor/schemas/payor_procedure_mapping.ex:50: ReimbursementMonitor.Schemas.PayorProcedureMapping.changeset/2
    test/schemas/payor_procedure_mapping_test.exs:28: (test)

完全披露,我已经在谷歌上搜索了很多,但我能够找到的示例是在插入 payor_procedure_mappings 时创建或构建付款人模式。

我觉得我在杂草丛生。我很想得到一些关于去哪里看的指示,这是我所问问题的好例子。

提前感谢您的帮助!

【问题讨论】:

【参考方案1】:

您可以尝试以下方法吗:

# the params that you pass in
%
  payor_id: 12,
  payor_procedure_id: "2342",
  payor_procedure_name: "That one thing we do to patients"


# the changeset function for payor_procedure_mappings
def changeset(model, params \\ %) do 
  model
  |> cast(params, all_params())
  |> validate_required(required())
end

我认为cast_assoc 的目的是创建新的关联记录,这就是为什么您只找到了这样做的示例。由于您只想将您正在创建的 payor_procedure_mappings 记录与现有的 payor 记录相关联,我相信您只需要设置适当的 id。

【讨论】:

【参考方案2】:

正如Ecto.Changeset.cast_assoc/3 的文档中所述

cast_assoc/3 匹配从数据库中提取的记录(预加载),并将其与从外部来源提供的参数进行比较。

也就是说,上面链接中的示例正是您所需要的:

- |> cast_assoc(:payor, params[:payor])
+ |> cast_assoc(:payor, with: &Payor.changeset/2)

该错误消息还暗示有一些 with: 预期的关键字键。

【讨论】:

以上是关于ecto 变更集为现有添加关联的主要内容,如果未能解决你的问题,请参考以下文章

我无法使用 ecto 的 `over/2` 函数在窗口中添加特定的 frame_clause

删除 Ecto 中的关联(多对多)

添加关联两个现有对象的对象

Rails 3.2 - 在现有表单上添加新关联

Elixir ecto 2 创建 many_to_many 关联

Ecto - 如何通过特定查询获取“has_many”