Marsel.V
Marsel.V

Reputation: 1044

phoenix ecto relationships on delete

There are two models: resource and metadata:

defmodule Myapp.Repo.Migrations.CreateResources do
  use Ecto.Migration

  def change do
    create table(:resources) do
      add :name, :string
      add :parent_id, references(:resources, on_delete: :delete_all)
      timestamps
    end
    create index(:resources, [:parent_id])
  end
end

defmodule Myapp.Repo.Migrations.CreateMetadata do
  use Ecto.Migration

  def change do
    create table(:metadata) do
      add :size, :integer
      add :resource_id, references(:resources)
      timestamps
    end
    create index(:metadata, [:resource_id])
  end
end

  schema "resources" do
    field :name, :string
    belongs_to :parent, Myapp.Resource
    has_one :metadata, Myapp.Metadata, on_delete: :delete_all
    has_many :childs, Myapp.Resource, foreign_key: :parent_id, on_delete: :delete_all

    timestamps()
  end

  schema "metadata" do
    field :size, :integer
    belongs_to :resource, Myapp.Resource

    timestamps()
  end

resource can have children, which parent_id = id. also has a resource metadata. Table Metadata has resource_id column. Now, I want to delete the resource with the remove all his children, and all associated metadata. when I write

Repo.delete resource

I get an error:

[error] #PID<0.441.0> running Myapp.Endpoint terminated
Server: localhost:4000 (http)
Request: POST /resources/%2Fdocs%2Ftest
** (exit) an exception was raised:
    ** (Postgrex.Error) ERROR (foreign_key_violation): update or delete on table "resources" violates foreign key constraint "metadata_resource_id_fkey" on table "metadata"

    table: metadata
    constraint: metadata_resource_id_fkey

Key (id)=(3) is still referenced from table "metadata".

that appears only when triggered to remove metadata from a resource children. If I try to delete a resource that has no childrens, then everything is working correctly.

Apparently, does not cause the removal of the children required callbacks removing metadata, so Postgres an error.

Whether it is possible to solve this problem without suffering from resource_id metadata metadata_id resource and without starting a recursive manual removal of all the children of the resource?

Upvotes: 2

Views: 3528

Answers (1)

Ali Naqvi
Ali Naqvi

Reputation: 116

:delete_all does not cascade to child records unless set via database migrations. To fix the problem make sure you change your metadata migration script line to

add :resource_id, references(:resources, on_delete: :delete_all)

Upvotes: 5

Related Questions