Ecto.InvalidChangesetError 在 has_many 关系上创建父级
Ecto.InvalidChangesetError on has_many relation when creating parent
我目前正在研究 Programming Phoenix 中的代码,但遇到了一个令我困惑的错误。
当我 运行 Rumbl.TestHelpers.insert_user
时出现以下错误
** (Ecto.InvalidChangesetError) could not perform insert because changeset is invalid.
* Changeset changes
%{name: "Some user", password: "supersecret", password_hash: "b$ZaSx6WcTZnrRGrneHsrNF.oMx8if3yMNssnx1B/lGBD5/GPj17Ym6", username: "user50853EBB5B75FC40"}
* Changeset params
%{"name" => "Some user", "password" => "supersecret", "username" => "user50853EBB5B75FC40"}
* Changeset errors
[videos: "is invalid"]
(ecto) lib/ecto/repo/schema.ex:121: Ecto.Repo.Schema.insert!/4
Rumbl.TestHelpers.insert_user
看起来像这样:
alias Rumbl.Repo
def insert_user(attrs \ %{}) do
changes = Dict.merge(%{
name: "Some user",
username: "user#{Base.encode16(:crypto.rand_bytes(8))}",
password: "supersecret"
}, attrs)
%Rumbl.User{}
|> Rumbl.User.registration_changeset(changes)
|> Repo.insert!()
end
Rumbl.User
:
defmodule Rumbl.User do
use Rumbl.Web, :model
schema "users" do
field :name, :string
field :username, :string
field :password, :string, virtual: true
field :password_hash, :string
has_many :videos, Rumbl.Video
timestamps
end
def changeset(model, params \ :invalid) do
model
|> cast(params, ~w(name username), [])
|> validate_length(:username, min: 1, max: 20)
|> unique_constraint(:username)
end
def registration_changeset(model, params) do
model
|> changeset(params)
|> cast(params, ~w(password), [])
|> validate_length(:password, min: 6, max: 100)
|> put_pass_hash()
end
defp put_pass_hash(changeset) do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{password: pass}} ->
put_change(changeset, :password_hash, Comeonin.Bcrypt.hashpwsalt(pass))
_ -> changeset
end
end
end
最后 Rumbl.Video
:
defmodule Rumbl.Video do
use Rumbl.Web, :model
schema "videos" do
field :url, :string
field :title, :string
field :description, :string
belongs_to :user, Rumbl.User
belongs_to :category, Rumbl.Category
timestamps()
end
@doc """
Builds a changeset based on the `struct` and `params`.
"""
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:url, :title, :description], [:category_id])
|> validate_required([:url, :title, :description])
|> assoc_constraint(:category)
end
end
如果有人能解释一下为什么会出现此错误,我将不胜感激。
问题出在您的变更集中:
def changeset(model, params \ :invalid) do
您的默认值为 :invalid
原子。我还关注了 Programming Phoenix,它说你可以传递 :empty 或 :invalid 原子。但是 :empty
不再被接受。所以我也测试了:invalid
.
问题是,当您不传递任何参数时,正如您在错误消息中看到的那样,:invalid 会使整个变更集无效。
在我的情况下,我想在你的情况下的解决方案是将 :invalid
更改为 %{}
,然后变更集参数将变为空映射,但仍然有效。它会在您的插入中工作。
解决这一切的是 运行 mix do deps.clean --all, deps.get, deps.compile && mix test
。
我目前正在研究 Programming Phoenix 中的代码,但遇到了一个令我困惑的错误。
当我 运行 Rumbl.TestHelpers.insert_user
** (Ecto.InvalidChangesetError) could not perform insert because changeset is invalid.
* Changeset changes
%{name: "Some user", password: "supersecret", password_hash: "b$ZaSx6WcTZnrRGrneHsrNF.oMx8if3yMNssnx1B/lGBD5/GPj17Ym6", username: "user50853EBB5B75FC40"}
* Changeset params
%{"name" => "Some user", "password" => "supersecret", "username" => "user50853EBB5B75FC40"}
* Changeset errors
[videos: "is invalid"]
(ecto) lib/ecto/repo/schema.ex:121: Ecto.Repo.Schema.insert!/4
Rumbl.TestHelpers.insert_user
看起来像这样:
alias Rumbl.Repo
def insert_user(attrs \ %{}) do
changes = Dict.merge(%{
name: "Some user",
username: "user#{Base.encode16(:crypto.rand_bytes(8))}",
password: "supersecret"
}, attrs)
%Rumbl.User{}
|> Rumbl.User.registration_changeset(changes)
|> Repo.insert!()
end
Rumbl.User
:
defmodule Rumbl.User do
use Rumbl.Web, :model
schema "users" do
field :name, :string
field :username, :string
field :password, :string, virtual: true
field :password_hash, :string
has_many :videos, Rumbl.Video
timestamps
end
def changeset(model, params \ :invalid) do
model
|> cast(params, ~w(name username), [])
|> validate_length(:username, min: 1, max: 20)
|> unique_constraint(:username)
end
def registration_changeset(model, params) do
model
|> changeset(params)
|> cast(params, ~w(password), [])
|> validate_length(:password, min: 6, max: 100)
|> put_pass_hash()
end
defp put_pass_hash(changeset) do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{password: pass}} ->
put_change(changeset, :password_hash, Comeonin.Bcrypt.hashpwsalt(pass))
_ -> changeset
end
end
end
最后 Rumbl.Video
:
defmodule Rumbl.Video do
use Rumbl.Web, :model
schema "videos" do
field :url, :string
field :title, :string
field :description, :string
belongs_to :user, Rumbl.User
belongs_to :category, Rumbl.Category
timestamps()
end
@doc """
Builds a changeset based on the `struct` and `params`.
"""
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:url, :title, :description], [:category_id])
|> validate_required([:url, :title, :description])
|> assoc_constraint(:category)
end
end
如果有人能解释一下为什么会出现此错误,我将不胜感激。
问题出在您的变更集中:
def changeset(model, params \ :invalid) do
您的默认值为 :invalid
原子。我还关注了 Programming Phoenix,它说你可以传递 :empty 或 :invalid 原子。但是 :empty
不再被接受。所以我也测试了:invalid
.
问题是,当您不传递任何参数时,正如您在错误消息中看到的那样,:invalid 会使整个变更集无效。
在我的情况下,我想在你的情况下的解决方案是将 :invalid
更改为 %{}
,然后变更集参数将变为空映射,但仍然有效。它会在您的插入中工作。
解决这一切的是 运行 mix do deps.clean --all, deps.get, deps.compile && mix test
。