Hi I'm new to Elixir and models/DB/APIs in general and I'm a bit lost in how to achieve the following:
I want to create an Event when I do a POST in my API to /v1/events
sending both tag_id
and gate_id
but I don't know how to handle this in my controller.
I've done this with simpler models where they just belong_to
one parent and I just pipe the build_assoc
in the changeset
, however, I'm lost on how to do it with multiple parents. I even tried a sketchy solution to manually create a changeset manually setting the gate_id
and tag_id
like this Event.changeset(%Event{"gate_id" => gate_id, "tag_id" => tag_id}, event_params)
however it errored out saying gate_id
and tag_id
are not part of the model.
So two questions: What is the correct way to handle this situation? Am I even close or my models are wrong?
Also does a POST to /v1/events
passing both of the ids sounds good or am I also lost in that one?
Models for reference
Tag model:
defmodule Tracker.Tag do
use Tracker.Web, :model
schema "tags" do
field :epc, :string
field :name, :string
field :is_active, :boolean, default: false
field :is_inside, :boolean, default: false
has_many :events, Tracker.Event
timestamps
end
@required_fields ~w(epc name is_active is_inside)
@optional_fields ~w()
def changeset(model, params \\ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
|> cast_assoc(:events, required: false)
end
end
Gate Model:
defmodule Tracker.Gate do
use Tracker.Web, :model
schema "gates" do
field :name, :string
field :direction_in, :boolean, default: false
field :antenna, :integer
belongs_to :reader, Tracker.Reader
has_many :events, Tracker.Event
timestamps
end
@required_fields ~w(name direction_in antenna)
@optional_fields ~w()
def changeset(model, params \\ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
|> cast_assoc(:events, required: false)
end
end
Update: More info
Event Model:
defmodule Tracker.Event do
use Tracker.Web, :model
schema "events" do
belongs_to :tag, Tracker.Tag
belongs_to :gate, Tracker.Gate
timestamps
end
@required_fields ~w()
@optional_fields ~w()
def changeset(model, params \\ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
end
end
Events Migration:
defmodule Tracker.Repo.Migrations.CreateV1.Event do
use Ecto.Migration
def change do
create table(:events) do
add :tag_id, references(:tags, on_delete: :nothing)
add :gate_id, references(:gates, on_delete: :nothing)
timestamps
end
create index(:events, [:tag_id])
create index(:events, [:gate_id])
end
end
Event controller create function (kind of lost on this one)
def create(conn, %{"event" => event_params}) do
gate_id = conn.assigns.gate_id
tag_id = conn.assigns.tag_id
changeset = Event.changeset(%Event{"gate_id" => gate_id, "tag_id" => tag_id}, event_params)
case Repo.insert(changeset) do
{:ok, event} ->
conn
|> put_status(:created)
|> put_resp_header("location", v1_event_path(conn, :show, event))
|> render("show.json", event: event)
{:error, changeset} ->
conn
|> put_status(:unprocessable_entity)
|> render(Tracker.ChangesetView, "error.json", changeset: changeset)
end
end
And the latest error I'm getting is
== Compilation error on file web/controllers/v1/event_controller.ex ==
** (CompileError) web/controllers/v1/event_controller.ex:22: unknown key "gate_id" for struct Tracker.Event
Can you post your error and your migration for Event
as well? I believe you need to add tag_id
and gate_id
to required fields in your model:
defmodule Tracker.Event do
use Tracker.Web, :model
schema "events" do
belongs_to :tag, Tracker.Tag
belongs_to :gate, Tracker.Gate
timestamps
end
@required_fields ~w(tag_id gate_id)
@optional_fields ~w()
def changeset(model, params \\ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
end
end