Hi,
I’m trying to post a list of (existing) products to a new order in Phoenix using Ecto. However, I am encountering the following issue:
INSERT INTO "receiving_orders_products" ("quantity","receiving_order_id") VALUES ($1,$2) [15, 39]
[debug] QUERY OK db=0.0ms
rollback []
[error] GenServer #PID<0.10237.0> terminating
** (Postgrex.Error) ERROR 23502 (not_null_violation) null value in column "product_id" violates not-null constraint
table: receiving_orders_products
column: product_id
The relationship is a Many-Many however I have configured it as a has_many due to the fact I needed an additional field and read that was the recommended way to handle it.
Below is my schema:
Order:
@required_fields ~w(supplier_name address_id)a
@optional_fields ~w(purchase_order_number type)a
schema "receiving_orders" do
field :supplier_name, :string
field :purchase_order_number, :string
field :type, :string
field :delivery_date, :utc_datetime
belongs_to :address, Rapport.Erp.Address
has_many :products, Rapport.Domain.ReceivingOrderProduct
timestamps()
end
def changeset(receiving_order, attrs) do
receiving_order
|> cast(attrs, @required_fields ++ @optional_fields)
|> cast_assoc(:products)
|> validate_required(@required_fields)
|> assoc_constraint(:address)
end
Order Product:
@required_fields ~w(quantity product_id receiving_order_id)a
@primary_key false
schema "receiving_orders_products" do
field :quantity, :integer
belongs_to(:receiving_order, Rapport.Domain.ReceivingOrder)
belongs_to(:product, Rapport.Erp.Product)
end
def changeset(purchase_order, attrs) do
purchase_order
|> cast(attrs, @required_fields)
|> validate_required(@required_fields)
end
This is how I am presenting it in the form:
<%= inputs_for f, :products, fn p -> %>
<%= inputs_for p, :product, fn product -> %>
<%= input product, :part_number, [disabled: true] %>
<%= input p, :quantity %>
<%= input product, :description, [disabled: true] %>
<% end %>
<hr />
<% end %>
I am then trying to save it here:
def create(attrs) do
IO.inspect(attrs)
%ReceivingOrder{}
|> ReceivingOrder.changeset(attrs)
|> IO.inspect()
|> Repo.insert()
end
And this is what both inspects looks like:
%{
"address" => %{"id" => "1"},
"address_id" => "1",
"products" => %{
"0" => %{"product" => %{"id" => "7"}, "quantity" => "15"},
"1" => %{"product" => %{"id" => "1"}, "quantity" => "10"}
},
"purchase_order_number" => "ORD03834",
"supplier_name" => "Some Supplier"
}
#Ecto.Changeset<
action: nil,
changes: %{
address_id: 1,
delivery_date: ~U[2020-08-19 20:25:43Z],
products: [
#Ecto.Changeset<
action: :insert,
changes: %{quantity: 15},
errors: [],
data: #Rapport.Domain.ReceivingOrderProduct<>,
valid?: true
>,
#Ecto.Changeset<
action: :insert,
changes: %{quantity: 10},
errors: [],
data: #Rapport.Domain.ReceivingOrderProduct<>,
valid?: true
>
],
purchase_order_number: "ORD03834",
supplier_name: "Some Supplier",
type: "Planned"
},
errors: [],
data: #Rapport.Domain.ReceivingOrder<>,
valid?: true
>
Any help appreciated. Thanks.