使用自定义 sql 而不是 table 返回 ecto 模型

Back an ecto model with custom sql instead of a table

我遇到了这种情况,我生成了一个带有一些自定义 sql 的中间 table。在我的应用程序中,我有一个模型指向那个中间 table。我有这个要求不使用中间 table,所以我试图弄清楚如何编写 Ecto 模型,以便它使用自定义 sql 加载数据型号。

重要提示:此模型仅用于读取 table 或 sql select 结果,因此我不必支持 insert/update/delete。这应该会大大简化我正在尝试做的事情。

这是我想要做的假模型:

defmodule EventBridge.C3poEvent do
  use Ecto.Schema
  import Ecto
  import Ecto.Query, only: [from: 1, from: 2]

  schema intermediate_table_name do
    field :id, :integer
    field :access_dates, :string
    field :action, :string                 
    field :counter, :integer
  end
end

让我们假设这个 sql 来获取数据:

select id, access_dates, action, counter from some_other_table 
  where some_conditions = true;

我需要做的是使用 sql 加载模型,而不是从 table 我的示例支持。

在我的脑海里,我在想我应该在模型中加入一个函数,比如:

def get_model(Model, some_conditions) do
  ...
end

并且在该函数中只需手动加载其中包含 sql 的模型。但我不相信这 a) 有意义或 b) 会产生一个我可以用来访问字段的模型。

也许我什至不应该使用模型?只是一个自定义结构,里面有 get_model 方法,不用担心用模式支持它?

再一次,请不要说我只读过这些数据。

如果我正确理解了您的需求,您只需要一种“升级”的结构。使用 Ecto.Schema.embedded_schema/1 很容易实现这一点。我会提供一个从我的生产代码示例中采纳的示例,显示验证和其他 Ecto 优点:

defmodule EventBridge.C3poEvent do
  use Ecto.Schema
  import Ecto.Changeset

  @required_fields ~w|id access_dates action|
  @fields ["counter" | @required_fields]

  @primary_key false
  embedded_schema do
    field :id, :integer
    field :access_dates, :string
    field :action, :string                 
    field :counter, :integer
  end

  def new(data) when is_map(data) do
    %__MODULE__{}
    |> cast(data, @fields)                 # free from Ecto
    |> validate_required(@required_fields) # free from Ecto
    |> apply_changes()                     # free from Ecto
  end

  ## `to_string` / interpolation support
  defimpl String.Chars, for: EventBridge.C3poEvent do
    def to_string(term) do
      "<[#{access_dates} #{action}(#{counter})]>"
    end
  end

  ## `inspect` support
  defimpl Inspect, for: EventBridge.C3poEvent do
    import Inspect.Algebra

    def inspect(%{
        id: id,
        access_dates: access_dates,
        action: action,
        counter: counter}, opts) do
      inner = [id: id, access_dates: access_dates,
               action: action, counter: counter]
      concat ["#EventBridge.C3poEvent<", to_doc(inner, opts), ">"]
    end
  end
end

以上是 Ecto 支持的任何“升级”结构的现成脚手架。一旦决定将其存储在数据库中,只需从 embedded_schema 切换到 schema 即可。