我有一个用户表,如:
email | username
---------------+----------
[email protected] |
[email protected] |
[email protected] |
我想通过
username
字段更新email
字段,只需在email
之前切片@
。 email | username
---------------+----------
[email protected] | 123
[email protected] | 123
[email protected] | haha
我尝试使用以下迁移:
defmodule MyApp.Repo.Migrations.AddDefaultUsernameForUsers do
use Ecto.Migration
import Ecto.Query
def up do
from(u in MyApp.User, update: [set: [username: String.split(u.email, "@") |> List.first ]])
|> MyApp.Repo.update_all([])
end
def down do
MyApp.Repo.update_all(MyApp.User, set: [username: nil])
end
end
但是在运行迁移时,出现以下错误:
$ mix ecto.migrate
** (Ecto.Query.CompileError) `List.first(String.split(u.email(), "@"))` is not a valid query expression
我该如何解决?
最佳答案
您将要进行两个单独的查询。第一个查询获取数据,进行所需的任何更改,然后第二个查询更新该数据。遵循以下原则
Repo.all(MyApp.User)
|> Enum.map(fn u ->
username =
u.email
|> String.split("@")
|> List.first()
Ecto.Changeset.cast(u, %{username: username})
end)
|> Repo.update_all()
关于为什么您无法做自己尝试做的事情,有两件事。
当您想在Ecto查询中使用Elixir函数或值时,通常必须使用pin运算符(
^
)。因此,如果要查询特定的ID,可以使用from(u in MyApp.User, where: u.id == ^12)
。因此,您的反应可能是尝试使用^List.first(String.split(u.email, "@"))
。但是,这将不起作用,因为...u
中的from(u in MyApp.User)
是数据库中的记录。您无法在Elixir代码中访问它。可能要使用fragment/1
进行尝试,但是您无法使用常规的Elixir函数操纵该值,直到使用上述示例将其实际从数据库中拉出为止。关于elixir - 如何在Ecto迁移中动态更新字段值?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44098069/