Search code examples
postgresqlphoenix-frameworkecto

Ecto Referencing Another Schema During Migration for Foreign-Key Relationship


We are working with a schema-based multi-tenancy database. We are on an Elixir stack, using Postgres, Ecto, and Triplex within a Phoenix Framework project.

We are using the default schema public to store common data, such as users and organisations. In particular, we have within the Organisations table a tenant_prefix column which we use to map a user to their tenancy.

Within a tenancy, we tenant-specific tables. As an example, we have a Products table. When a new organisation is created, we use Triplex to create the schema and run tenant migrations, which create the tenant-specific tables such as Products.

As a visual, the database looks like this:

- app_database
  - public
    - users
    - organisations
    - organisations_users

  - tenant1
    - products
    - (other tables...)

  - tenant2
     - products
     - (other tables...)

The Products migration looks like this.

 1 defmodule App.Repo.Migrations.CreateProducts do
 2  use Ecto.Migration
 3
 4  def change do
 5    create table(:products) do
 6      add :title, :string
 7      add :description, :string
 8      add :organisation_id, references(:organisations, on_delete: :nothing), null: false
 9
10      timestamps()
11    end
12
13    create index(:products, [:organisation_id])
14  end
15 end

Right now, it is failing to run because of the Line 8. The erorr that is reported is: ERROR 42P01 (undefined_table) relation "59ef85c702d24d0fac5c7e425d0d3d44.organisations" does not exist

The tenant prefix is a UUID.

So to summarise, we are wondering how to reference the public.organisations table to define the foreign key relationship in tenant.products.


Solution

  • I am not sure if it helps but I think that you should be using prefix option with your references. You can checkout the docs for further references.

    https://hexdocs.pm/ecto_sql/Ecto.Migration.html#references/2

    As of my understanding line 8 should be something like this

    add :organisation_id, references(:organisations, on_delete: :nothing, prefix: "public"), null: false
    

    As for the syntax, use :public if "public" results in a syntax error.

    Why you need this?

    Normally when prefix defaults to nil and in that case ecto certainly prefixes your current schema name with the table name, which is happening in your case. By setting this prefix option you can set a custom value based on your requirements.

    Please do comment if it works. Since I am also curious... 😅