我有一个用户表,如:

     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/

10-12 23:41