我必须处理无法更改的数据库设置,并且必须使用特定的SQL查询来计算不是表中字段的值(例如SELECT *, 1 AS x FROM products;
x是我需要的字段)。如何在Ecto中进行这项工作?这是我的方法以及遇到的问题:
$ mix phx.new testapp
$ cd testapp
$ mix ecto.create
$ mix phx.gen.html Shops Product products name price:float
$ mix ecto.migrate
之后,我创建了两个产品。
我将虚拟x
字段添加到product
:
lib / testapp / shops / product.ex
defmodule Testapp.Shops.Product do
use Ecto.Schema
import Ecto.Changeset
schema "products" do
field :name, :string
field :price, :float
field :x, :integer, virtual: true # <-----
timestamps()
end
@doc false
def changeset(product, attrs) do
product
|> cast(attrs, [:name, :price])
|> validate_required([:name, :price])
end
end
并且我将以下功能添加到Testapp.Shops
:
def execute_and_load(sql, params, model) do
result = Ecto.Adapters.SQL.query!(Repo, sql, params)
Enum.map(result.rows, &Repo.load(model, {result.columns, &1}))
end
def list_products_with_x do
sql = "SELECT *, 1 AS x FROM products;" # <- simplified
execute_and_load(sql, [], Testapp.Shops.Product)
end
1 AS x
,整个SQL查询只是一个简化示例!在实际的应用程序中,我必须使用SQL查询来调用存储过程进行计算,该计算会将值存储在x
中。因此,会有一些我无法使用Ecto本身创建的SQL。我必须使用SQL。
SQL查询为每个条目传递x
的值,但product
将x
列出为nil
。我怎么解决这个问题?如何在virtual
中填写execute_and_load/3
字段?
iex(1)> Testapp.Shops.list_products_with_x
[debug] QUERY OK db=1.3ms queue=2.2ms idle=8177.7ms
SELECT *, 1 AS x FROM products; []
[
%Testapp.Shops.Product{
__meta__: #Ecto.Schema.Metadata<:loaded, "products">,
id: 1,
inserted_at: ~N[2020-02-12 07:29:36],
name: "Apple",
price: 0.5,
updated_at: ~N[2020-02-12 07:29:36],
x: nil
},
%Testapp.Shops.Product{
__meta__: #Ecto.Schema.Metadata<:loaded, "products">,
id: 2,
inserted_at: ~N[2020-02-12 07:29:47],
name: "Orange",
price: 0.75,
updated_at: ~N[2020-02-12 07:29:47],
x: nil
}
]
我愿意为给定的问题寻求替代解决方案。我无法在我的Elixir程序中计算x
的值。我必须使用SQL来计算它,我想使用Ecto。
一种更好的方法是选择结构所需的所有内容,然后将其移至Ecto.Struct
。我的方法如下:
def get_products() do
query = from p in Products,
select: %{name: p.name, price: p.price, x: fragment("1")}
query
|> Repo.all()
|> Enum.map(fn el -> struct(Products, el) end)
end
这种方法的优点是我不使用原始字符串查询。您的计算应放在片段部分的内部。