凭证变更集未插入数据库
Credential changeset not inserted into database
当我创建一个新用户时,我会经历以下过程,
accounts.ex
def register_user(attrs \ %{}) do
%User{}
|> User.registration_changeset(attrs)
|> Repo.insert()
end
user.ex
def changeset(user, attrs) do
user
|> cast(attrs, [:name, :username])
|> validate_required([:name, :username])
|> validate_length(:username, min: 1, max: 20)
|> unique_constraint(:username)
end
def registration_changeset(user, params) do
user
|> changeset(params)
|> Ecto.Changeset.cast_assoc(:credential, with: &Credential.changeset/2)
end
credential.ex
def changeset(credential, attrs) do
credential
|> cast(attrs, [:email, :password])
|> validate_required([:email, :password])
|> validate_length(:password, min: 6, max: 100)
|> unique_constraint(:email)
|> put_hash()
end
defp put_hash(changeset) do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{password: pass}} ->
put_change(changeset, :password_hash, Bcrypt.hash_pwd_salt(pass))
_ ->
changeset
end
end
测试时我得到以下信息,
iex(0)> Accounts.register_user(%{name: "Test", username: "test", email: "test@here.com", password: "12345678"})
[debug] QUERY OK db=17.2ms queue=1.7ms
虽然我注意到 Credentials table 是空的,但在尝试查看用户时,
iex(1)> Repo.all User
[debug] QUERY OK source="users" db=1.5ms decode=0.1ms queue=0.2ms
[
%App.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
credential: #Ecto.Association.NotLoaded<association :credential is not loaded>,
id: 1,
inserted_at: ~N[2019-06-21 16:39:46],
...
]
iex(2)> Repo.all Credential
[debug] QUERY OK source="credentials" db=5.5ms queue=0.1ms
[]
我做错了什么?
cast_assoc
需要多一层嵌套。
在您的示例中,cast_assoc(:credential, ...)
尝试在您的参数中找到 credentials
键,并且 在 下的参数将被尝试转换为 Credential
变更集。
这是来自 docs 的示例:
%{"name" => "john doe", "addresses" => [
%{"street" => "somewhere", "country" => "brazil", "id" => 1},
%{"street" => "elsewhere", "country" => "poland"},
]}
它是用:
User
|> Repo.get!(id)
|> Repo.preload(:addresses) # Only required when updating data
|> Ecto.Changeset.cast(params, [])
|> Ecto.Changeset.cast_assoc(:addresses, with: &MyApp.Address.changeset/2)
如您所见,输入 JSON 数据结构具有键 "addresses"
,然后将其转换为 :addresses
所以你的数据应该有下一个结构:
%{
name: "Test",
username: "test",
credential: %{
email: "test@here.com",
password: "12345678"
}
}
当然,这不是你想在注册过程中看到的!
所以,这里有两个解决方案:
扔掉所有这些 Credential 人员,并通过内部注册变更集保持简单。
手动建立关联 - 使用 build_assoc
或 put_assoc
使用 inputs_for/4
函数附上您的电子邮件和密码输入。
<%= inputs_for f, :credential, fn cf -> %>
<%= email_input cf, :email %>
<%= password_input cf, :password %>
<% end %>
它将为您预加载凭据。 Here's the link
当我创建一个新用户时,我会经历以下过程,
accounts.ex
def register_user(attrs \ %{}) do
%User{}
|> User.registration_changeset(attrs)
|> Repo.insert()
end
user.ex
def changeset(user, attrs) do
user
|> cast(attrs, [:name, :username])
|> validate_required([:name, :username])
|> validate_length(:username, min: 1, max: 20)
|> unique_constraint(:username)
end
def registration_changeset(user, params) do
user
|> changeset(params)
|> Ecto.Changeset.cast_assoc(:credential, with: &Credential.changeset/2)
end
credential.ex
def changeset(credential, attrs) do
credential
|> cast(attrs, [:email, :password])
|> validate_required([:email, :password])
|> validate_length(:password, min: 6, max: 100)
|> unique_constraint(:email)
|> put_hash()
end
defp put_hash(changeset) do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{password: pass}} ->
put_change(changeset, :password_hash, Bcrypt.hash_pwd_salt(pass))
_ ->
changeset
end
end
测试时我得到以下信息,
iex(0)> Accounts.register_user(%{name: "Test", username: "test", email: "test@here.com", password: "12345678"})
[debug] QUERY OK db=17.2ms queue=1.7ms
虽然我注意到 Credentials table 是空的,但在尝试查看用户时,
iex(1)> Repo.all User
[debug] QUERY OK source="users" db=1.5ms decode=0.1ms queue=0.2ms
[
%App.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
credential: #Ecto.Association.NotLoaded<association :credential is not loaded>,
id: 1,
inserted_at: ~N[2019-06-21 16:39:46],
...
]
iex(2)> Repo.all Credential
[debug] QUERY OK source="credentials" db=5.5ms queue=0.1ms
[]
我做错了什么?
cast_assoc
需要多一层嵌套。
在您的示例中,cast_assoc(:credential, ...)
尝试在您的参数中找到 credentials
键,并且 在 下的参数将被尝试转换为 Credential
变更集。
这是来自 docs 的示例:
%{"name" => "john doe", "addresses" => [
%{"street" => "somewhere", "country" => "brazil", "id" => 1},
%{"street" => "elsewhere", "country" => "poland"},
]}
它是用:
User
|> Repo.get!(id)
|> Repo.preload(:addresses) # Only required when updating data
|> Ecto.Changeset.cast(params, [])
|> Ecto.Changeset.cast_assoc(:addresses, with: &MyApp.Address.changeset/2)
如您所见,输入 JSON 数据结构具有键 "addresses"
,然后将其转换为 :addresses
所以你的数据应该有下一个结构:
%{
name: "Test",
username: "test",
credential: %{
email: "test@here.com",
password: "12345678"
}
}
当然,这不是你想在注册过程中看到的!
所以,这里有两个解决方案:
扔掉所有这些 Credential 人员,并通过内部注册变更集保持简单。
手动建立关联 - 使用
build_assoc
或put_assoc
使用 inputs_for/4
函数附上您的电子邮件和密码输入。
<%= inputs_for f, :credential, fn cf -> %>
<%= email_input cf, :email %>
<%= password_input cf, :password %>
<% end %>
它将为您预加载凭据。 Here's the link