【问题标题】:Elixir Ecto Issue - Can't make an association between two tables?Elixir Ecto 问题 - 无法在两个表之间建立关联?
【发布时间】:2022-02-03 00:22:34
【问题描述】:

我正在尝试在表格之间建立关联。

我目前有一个 "users" 表,其中包含以下字段:

id email password first_name last_name role
1 teacher@example.io hash Bob White teacher
2 student@example.io hash Joe Brown student

我正在尝试实现两种新的用户类型:教师学生

因此,我想为两种用户类型创建两个不同的表,它们将像这样引用主表:

  • 教师
id user_id first_name last_name
1 1 (reference to "users") Bob White
  • 学生
id user_id first_name last_name
1 2 (reference to "users") Joe Brown

我生成了一个新的迁移并创建了“教师”表,如下所示:

create table("teachers") do
   add :user_id, references("users")
   add :first_name, :string
   add :last_name, :string
end

然后我为教师表定义了一个架构,如下所示:

schema "teachers" do
   field :first_name, :string
   field :last_name, :string

   belongs_to :user, User

最后,我将它添加到用户架构中:

   has_many :teachers, Teacher

我现在已经成功地添加了我想要的列的教师表。问题是,我的注册表只包含一个变更集。用户模式进行所有验证,并在注册成功时将用户添加到“用户”表中。但“教师”表仍然是空的。用注册用户的信息填充教师表的最佳做法是什么?

【问题讨论】:

  • 您可以做的是,在用户的有效变更集上,您填充教师或学生条目

标签: elixir schema phoenix-framework ecto phoenix-live-view


【解决方案1】:

首先要知道的是没有一个“最佳实践”。

根据我的经验,有两种不同的方法:

  1. 在您的 User.changeset 函数中使用 cast_assoc
  2. 在您的create_user 函数中,使用Ecto.Multi 创建:user,然后使用此用户ID 创建teacherstudent

这两个都不是“更好”的。使用对您感觉更好的一种。

我现在将更详细地解释这两种方法:

1。 cast_assoc 方法

在您的 accounts.ex 上下文中,您将拥有:

def create_user(attrs) do
  %User{}
  |> User.creation_changeset(attrs)
  |> Repo.insert()
end

attrs 映射如下所示:

%{
  password: "unhashed-password"
  student: %{
    first_name: "Bob",
    last_name: "Smith"
  }
}

或者这个:

%{
  password: "unhashed-password"
  teacher: %{
    first_name: "Bob",
    last_name: "Smith"
  }
}

我决定创建一个仅用于创建用户的变更集函数,因为有时我可能只想更新用户而不更新相关的教师/学生。所以,在user.ex 文件中,我有这个:

def creation_changeset(user, attrs) do
  User
  |> cast(attrs, @user_fields_to_cast)
  |> cast_assoc(:teacher, with: &Teacher.user_creation_changeset/2)
  |> cast_assoc(:student, with: &Student.user_creation_changeset/2)
end

差不多就是这样。

create_user 函数应返回一个 {:ok, %User{}}(或 {:error, %Ecto.Changeset{}})元组,其中在用户上加载了 teacherstudent 关联(教师或学生应为 nil)。

您不必使用专门的“创建”变更集,但我发现它通常会使这更容易。

2。 Ecto.Multi 方法

如果您这样做,则无需使用cast_assoc,您可以保持用户/教师/学生模式不变。相反,上下文函数看起来像这样:

alias Ecto.Multi

def create_user(attrs) do
  {teacher_attrs, attrs} = Map.pop(attrs, :teacher)
  {student_attrs, attrs} = Map.pop(attrs, :student)

  Multi.new()
  |> Multi.insert(:user, User.changeset(%User{}, attrs)
  |> Multi.run(:teacher, fn repo, %{user: user} ->
    if teacher_attrs do
      %Teacher{}
      |> Teacher.changeset(Map.put(teacher_attrs, :user_id, user.id))
      |> repo.insert()
    else
      {:ok, nil}
    end
  end)
  |> Multi.run(:student, fn repo, %{user: user} ->
    if student_attrs do
      %Student{}
      |> Student.changeset(Map.put(student_attrs, :user_id, user.id))
      |> repo.insert()
    else
      {:ok, nil}
    end
  end)
end

这将返回(在快乐的道路上),这个:

{:ok, %{user: user, student: student, teacher: teacher}}

学生或老师都将为零。

好的,我已经完成了。

我在浏览器中输入了所有这些内容,没有进行任何测试,所以要小心拼写错误、不匹配的括号和其他小问题!这只是说明性的。

【讨论】:

    猜你喜欢
    • 2023-03-30
    • 1970-01-01
    • 2019-10-18
    • 2016-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多