has_many关联,在一列中存储多个关联

时间:2017-05-23 21:55:52

标签: elixir phoenix-framework ecto

我有两个表,usersroles。我想要做的是在它们之间创建简单的关联,users可以有多个roles

在数据库中它看起来应该是这样的:

作用

 id | name
-----------
  1 | ADMIN
  2 | USER

用户

id | email         | username | roles
-------------------------------------
 1  test@gmail.com    test      [1,2]

到目前为止,我提供了以下代码:

def change do
    create table(:users) do
      add :email, :string
      add :username, :string
      add :roles, references(:roles)
    end
  end

schema "users" do
    field :email, :string
    field :username, :string
    has_many :roles, TimesheetServer.Role

    timestamps()
  end

  def changeset(struct, params \\ %{}) do
     struct
      |> cast(params, [:email, :username])
      |> validate_required([:email, :username])
  end

def change do
    create table(:roles) do
      add :name, :string
      timestamps()
    end
  end

schema "roles" do
    field :name, :string
    belongs_to :users, TimesheetServer.User
    timestamps()
  end

然后我试图像这样种子数据库:

User.changeset(
    %User{},%{email: "test@gmail.com", username: "test", roles: [
        %Role{name: "ADMIN"},
        %Role{name: "USER"}
    ]})

    |> Repo.insert

不幸的是,这不会在user表中创建角色列表,也不会在roles表中保存角色。

非常感谢任何帮助或提示!

1 个答案:

答案 0 :(得分:2)

如评论中所述,上述关系必须定义为many_to_many。为实现这一目标,实际上有两种方式。

首先是将角色架构定义为embedded_schema,在这种情况下,它将嵌入在父(用户)内。这意味着不会为角色架构创建db表。

第二个选项是通过关联表加入它们。这看起来如下:

迁移:

def change do
 create table(:users) do
   add :email, :string
   add :username, :string
 end

 create table(:roles) do
   add :name, :string, null: false
 end

 create table(:users_roles, primary_key: false) do
  add :user_id, references(:users, on_delete: :delete_all)
  add :role_id, references(:roles, on_delete: :delete_all)
 end

 create unique_index(:users_roles, [:user_id, :role_id])
end

用户架构:

 schema "users" do
  field :email, :string
  field :username, :string
  many_to_many :roles, TimesheetServer.Role, join_through: "users_roles"
 end

  def changeset(struct, params \\ %{}) do
   struct
    |> cast(params, [:email, :username])
    |> validate_required([:email, :username])
  end
end

角色架构:

   schema "roles" do
     field :name, :string
     many_to_many :users, TimesheetServer.User, join_through: "users_roles"
   end

   def changeset(struct, params \\ %{}) do
     struct
     |> cast(params, [:name])
     |> validate_required([:name])
   end

现在可以通过以下方式构建关联:

    user = Repo.get!(User, user_id) |> Repo.preload(:roles)
    role = Repo.get!(Role, role_id)

    user_changeset = Changeset.change(user)
                         |> Changeset.put_assoc(:roles, [role | user.roles])

这将导致有三个表:

用户:

id | email         | username |
-------------------------------
 1  test@gmail.com    test     

作用:

 id | name
-----------
  1 | ADMIN
  2 | USER

users_roles:

user_id | role_id
------------------
      1 | 1
------------------
      1 | 2