Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Elixir 在EXTO中同时创建具有所需外部\u id的父、子嵌套关联_Elixir_Phoenix Framework_Ecto - Fatal编程技术网

Elixir 在EXTO中同时创建具有所需外部\u id的父、子嵌套关联

Elixir 在EXTO中同时创建具有所需外部\u id的父、子嵌套关联,elixir,phoenix-framework,ecto,Elixir,Phoenix Framework,Ecto,我有一个父组件和一个子组件。我想在创建子对象的同时创建父对象,因为没有子对象,父对象就不能存在。具体来说,我有一个订阅,其中有许多服务 如果我的子模型有一个必填字段作为外部约束,那么如何同时创建这两个模型?我的变更集中出现错误,表明parent.id不能为空 我知道我可以做Repo.insert!(Subscription)然后使用Subscription.id创建服务变更集,但我想知道是否可以同时创建这两个变更集 我的父变更集和子变更集如下所示: 家长(订阅) 儿童(服务) 这是一个鸡蛋鸡问题

我有一个父组件和一个子组件。我想在创建子对象的同时创建父对象,因为没有子对象,父对象就不能存在。具体来说,我有一个
订阅
,其中
有许多
服务

如果我的子模型有一个必填字段作为外部约束,那么如何同时创建这两个模型?我的变更集中出现错误,表明parent.id不能为空

我知道我可以做
Repo.insert!(Subscription)
然后使用
Subscription.id
创建
服务
变更集,但我想知道是否可以同时创建这两个变更集

我的父变更集和子变更集如下所示:

家长(订阅)

儿童(服务)


这是一个鸡蛋鸡问题:数据库引擎正在将唯一ID分配给主记录。因此,不可能在一个事务中执行此操作

唯一的可能是自己处理主表上的
ID
键,通过DB内部函数生成GUID(如MySQL或PostgreSQL)。在这种情况下,可以提前调用此函数并显式设置
ID


不过,我不推荐后一种方法。

这是一个老问题,但如果有人像我一样来到这里,我会回答这个问题

def changeset(%Subscription{} = subscription, attrs) do
  subscription
  |> cast(attrs, [...])
  |> ...
  |> cast_assoc(:services, required: true)
  |> ...
end

def create_subscription(attrs \\ %{}) do
  %Subscription{}
  |> Subscription.changeset(attrs)
  |> Repo.insert()
end

正如Aleksei正确指出的那样,这是一个鸡和蛋的问题,因为在您准备声明时,父实体id不可用于外部组织协会。在我看来,您所要求的只有通过使用驱动事务才能实现。事务将确保即使成功插入父实体,但其中一个子实体未通过验证检查,整个事务也将回滚,不会出现不一致

这是总的想法

  • 首先,在您的上下文模块中,定义一个用于执行事务性插入的新函数:
  • 然后添加一个新的POST/create处理程序,以便在父实体的REST控制器中为嵌套的父/子结构使用新定义的函数:

订阅中的哪个字段取决于此处的服务?你能发布准确的错误信息吗?
def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:start_time, :frequency, :subscription_id])
    |> validate_required([:subscription_id])
    |> foreign_key_constraint(:subscription_id)
end
def changeset(%Subscription{} = subscription, attrs) do
  subscription
  |> cast(attrs, [...])
  |> ...
  |> cast_assoc(:services, required: true)
  |> ...
end

def create_subscription(attrs \\ %{}) do
  %Subscription{}
  |> Subscription.changeset(attrs)
  |> Repo.insert()
end
  def create_parent_with_children(attrs \\ %{}) do
    Ecto.Multi.new()
    # Insert the parent entity
    |> Ecto.Multi.insert(:parent_entity, Parent.changeset(%Parent{}, attrs))
    # then use the newly created  parent's id to insert all children
    |> Ecto.Multi.merge(fn %{parent_entity: par} ->
      attrs["children"]
      |> Enum.reduce(Ecto.Multi.new, fn child, multi ->
        # important: name each child transaction with a unique name
        child_multi_id = :"#{child["uniq_field1"]}_#{child["uniq_field2"]}"
        Ecto.Multi.insert(multi, child_multi_id, %Child{parent_id: par.id}
          |> Child.changeset(child))
      end)
    end)
    |> Repo.transaction()
  end
def create(conn, %{"parent" => %{"children" => _} = parent_attrs}) do
    with {:ok, %{parent: parent}} <- Context.create_parent_with_children(parent_attrs) do
... (same body as create_parent/2)
  def call(conn, {:error, failed_tran, %Ecto.Changeset{} = changeset, _parent}) do
    conn
    |> put_resp_header("x-failed-transaction", Atom.to_string(failed_tran))
    |> put_status(:unprocessable_entity)
    |> put_view(InterfixWeb.ChangesetView)
    |> render("error.json", changeset: changeset)
  end