Elixir Ecto - 与 3 个或更多表的关联

问题描述

如果我有 profile,item,location 表:

  • profile 有很多 location
  • item一个 location
  • locationprofile 中的item 不会重叠。

我应该如何设计 location 表? 以下有效吗? location 属于 2 个表?

schema "profiles" do
...
has_many :location,Location
end

schema "items" do
...
has_one :location,Location
end

schema "locations" do
...
belongs_to :profile,Profile
belongs_to :item,Item
end

还是应该有 2 个 location 表?

解决方法

这是一个非常主观的问题,取决于您将如何使用 locations 表中的数据。这通常是 polymorphic associations in Ecto 的推荐方法,具有额外的数据库约束,强制始终在位置记录上设置 profile_iditem_id 之一。

defmodule MyApp.Repo.Migrations.CreateLocations do
  use Ecto.Migration

  def change do
    create table(:locations) do
      add :profile_id,references(:profiles,on_delete: :delete_all)
      add :item_id,references(:items,on_delete: :delete_all)
    end

    create constraint(:locations,:at_least_one_ref,check: "profile_id is not null or item_id is not null")
    create index(:locations,[:profile_id,:item_id])
  end
end

然后在变更集中,您可以验证此约束:

def changeset(location,attrs) do 
  location
  |> cast(attrs,:item_id])
  |> check_constraint(:profile_id,name: :at_least_one_ref)
end

另一种非常适用于 Ecto 但效果很好的方法,特别是因为您提到配置文件和项目之间没有位置重叠是使用使用 embeds_oneembeds_many 的嵌入式关联。如果您使用 Postgres,您甚至应该能够根据需要在 JSONB 列上创建索引。