Phoenix : can't insert foreign keys in Postgres

Hi,
I’m currently trying ecto and i got a problem.
I generated REST JSON API, with foreign key constraints using CLI.

I have Topic and User
My schemas :

defmodule Api.Forum.Topic do
  use Ecto.Schema
  import Ecto.Changeset

  schema "topics" do
    field(:body, :string)
    field(:title, :string)
    field(:user_id, :integer, null: false)

    timestamps()
  end

  @doc false
  def changeset(topic, attrs) do
    topic
    |> cast(attrs, [:title, :body])
    |> validate_required([:title, :body])
  end
end
defmodule Api.Users.User do
  use Ecto.Schema
  import Ecto.Changeset

  schema "users" do
    field(:name, :string)
    has_many(:topics, EkklesiaApi.Forum.Topic)

    timestamps()
  end

  @doc false
  def changeset(user, attrs) do
    user
    |> cast(attrs, [:name])
    |> validate_required([:name])
  end
end

and migrations :

defmodule Api.Repo.Migrations.CreateUsers do
  use Ecto.Migration

  def change do
    create table(:users) do
      add :name, :string

      timestamps()
    end

  end
end
defmodule Api.Repo.Migrations.CreateTopics do
  use Ecto.Migration

  def change do
    create table(:topics) do
      add :title, :string
      add :body, :text
      add :user_id, references(:users, on_delete: :nothing)

      timestamps()
    end

    create index(:topics, [:user_id])
  end
end

Can’t understand why inserting {title: “Foo”, body: “bar”, user_id: 1} only insert title and body

Thanks

You are missing the user_id part there. :slight_smile:

|> cast(attrs, [:title, :body, :user_id])
|> validate_required([:title, :body, :user_id])
2 Likes

Thanks, i’m stupid… ^^

Is there any reason that phoenix does not add these fields by default in validation ?

Nah, easy to miss. ^.^

It’s to encourage the usage of manually/on-site using things like put_assoc, which I personally don’t prefer as that tends to require having to load the association, so I do it the way that you are trying to too personally. :slight_smile:

With my limited experience i can’t say if one way is better than an other.
BTW thanks for the quick answer :smile:

1 Like

Heh, honestly I’d say that if you do have both rows already queried then put_assoc is better (if using a reference in the schema, otherwise not) as it will check types and such locally, otherwise setting directly is good as the DB will check it anyway for you. :slight_smile:

Heh, I try. ^.^

Is there any reason that phoenix does not add these fields by default in validation ?

Casting foreign keys from outside data can open you up for some nasty vulnerabilities.

I saw a project that casted something like shop_id in their changesets and I could do whatever I wanted with them since I was practically “the owner” of every shop in their database.

The fact that it’s so easy to do in ecto + phoenix is a bit worrisome.

1 Like

Yeah don’t just put everything in a changeset, only cast the things that you explicitely want, otherwise anyone outside can just submit whatever data they want to your database anyway! I kind of wish that Phoenix/Ecto didn’t generate changeset functions in their schema’s to be honest…