使用 DecorateX 计算虚拟字段的值

Calculating the value of virtual fields with DecorateX

我正在尝试使用 Decoratex 库来填充我的用户模型中的一些虚拟字段,但我无法避免以下错误:

no function clause matching in Decoratex.decorate/1
  This functions just call the configured function to each field passing
  the model structure it self and it store the result in the virtual field.
  """
  @spec decorate(nil) :: nil
  def decorate(nil), do: nil
  @spec decorate(struct) :: struct
  def decorate(%module{} = element) do
    module.__decorations__ |> Enum.reduce(element, &do_decorate/2)
  end

例如,我正在尝试向模型结构添加一个 user_age 键值。型号是:

defmodule InformAPI.UTL.User_take_list do
  use Ecto.Schema
  use Decoratex.Schema
  # alias InformAPI.UTL.UTLHelpers


  import Ecto.Changeset

  decorations do
    decorate_field :user_age, :integer,  &UTLHelpers.calculate_user_age/1

  end


  schema "user_take_lists" do
    field :date_of_birth, :date
    field :first_name, :string
    field :last_name, :string
    field :user_id, :string
    field :needs_senior_review, :boolean
    field :stable, :boolean
    field :list_date, :date
    belongs_to :consultant, InformAPI.UTL.User_take_list_consultant

timestamps()
decorations()
end


  def changeset(user_take_list, attrs) do
    user_take_list
    |> cast(attrs, [:user_id, :first_name, :date_of_birth, :last_name, :needs_senior_review, :list_date, :stable, :consultant_id])
    |> validate_required([:user_id, :first_name, :date_of_birth, :last_name, :needs_senior_review, :consultant_id, :stable])
  end
end

calculate_user_age 函数当前配置为仅 return 结果:

defmodule InformAPI.UTL.UTLHelpers do
  alias InformAPI.UTL.User_take_list

  def calculate_user_age(struct) do
    x = [1,2,3,4,5,6]
    Enum.count(x)
  end
end

并且在我的控制器的 Index 函数上调用了所需的 Decoratex.decorate:

defmodule InformAPIWeb.User_take_listController do
  import Ecto.Query, warn: false
  use InformAPIWeb, :controller
  use Filterable.Phoenix.Controller

  alias InformAPI.UTL
  alias InformAPI.UTL.User_take_list
  alias InformAPI.Repo

  action_fallback InformAPIWeb.FallbackController

  filterable do
    filter review(query, value, _conn) do
      IO.inspect(query)
      query |> where(needs_senior_review: ^value)
    end

    @options param: :dateFrom, cast: :date 
      filter dateFrom(query, value, _conn) do
        query |> where([d], d.list_date >= ^value)
    end

    @options param: :dateTo, cast: :date 
      filter dateTo(query, value, _conn) do
        query |> where([d], d.list_date <= ^value)
      end

    @options param: :conid, cast: :integer
        filter conID(query, value, _conn) do
          query  |> where([d], d.consultant_id <= ^value)         
    end

  end



 def index(conn, params) do
    with {:ok, query, filter_values} <- apply_filters(User_take_list, conn),
         user_take_lists                       <- Repo.all(query) |>Decoratex.decorate,

     do: render(conn, "index.json", user_take_lists: user_take_lists, meta: filter_values)
  end


  def show(conn, %{"id" => id}) do
    user_take_list = UTL.get_user_take_list!(id)
    render(conn, "show.json", user_take_list: user_take_list)
  end

end

我是 Elixir 的新手,我无法找出这个错误的本质。如果我检查该结构,我可以看到一个值为 nil 的 user_age 字段,但我不知道问题是由我自己的辅助函数引起的还是由我使用 DecorateX.decorate.[= 的方式引起的16=]

按照评论中的要求,堆​​栈跟踪的完整错误:

[info] GET /api/user_take_list
[debug] Processing with InformAPIWeb.User_take_listController.index/2
  Parameters: %{}
  Pipelines: [:api]
[debug] QUERY OK source="user_take_lists" db=0.0ms decode=16.0ms
SELECT m0."id", m0."date_of_birth", m0."first_name", m0."last_name", m0."user_id", m0."needs_senior_review", m0."stable", m0."list_date", m0."consultant_id", m0."inserted_at", m0."updated_at" FROM "user_take_lists" AS m0 []
[info] Sent 500 in 141ms
[error] #PID<0.390.0> running InformAPIWeb.Endpoint (cowboy_protocol) terminated
Server: localhost:4000 (http)
Request: GET /api/user_take_list
** (exit) an exception was raised:
    ** (FunctionClauseError) no function clause matching in Decoratex.decorate/1
        (decoratex) lib/decoratex.ex:145: Decoratex.decorate([%InformAPI.UTL.User_take_list{__meta__: #Ecto.Schema.Metadata<:loaded, "user_take_lists">, consultant: #Ecto.Association.NotLoaded<association :consultant is not loaded>, consultant_id: 3, date_of_birth: ~D[1987-02-13], first_name: "Hal", id: 1, inserted_at: ~N[2018-09-19 10:58:49.194000], last_name: "Bradley", list_date: ~D[2018-09-13], needs_senior_review: true, user_age: nil, user_id: "D00001", stable: true, updated_at: ~N[2018-09-19 10:58:49.194000]}, %InformAPI.UTL.User_take_list{__meta__: #Ecto.Schema.Metadata<:loaded, "user_take_lists">, consultant: #Ecto.Association.NotLoaded<association :consultant is not loaded>, consultant_id: 3, date_of_birth: ~D[1987-02-13], first_name: "Isobel", id: 2, inserted_at: ~N[2018-09-19 10:58:49.211000], last_name: "Rogers", list_date: ~D[2018-09-13], needs_senior_review: false, user_age: nil, user_id: "D00001", stable: true, updated_at: ~N[2018-09-19 10:58:49.211000]}, %InformAPI.UTL.User_take_list{__meta__: #Ecto.Schema.Metadata<:loaded, "user_take_lists">, consultant: #Ecto.Association.NotLoaded<association :consultant is not loaded>, consultant_id: 2, date_of_birth: ~D[1987-02-13], first_name: "Oliver", id: 3, inserted_at: ~N[2018-09-19 10:58:49.220000], last_name: "Barton", list_date: ~D[2018-09-13], needs_senior_review: true, user_age: nil, user_id: "D00001", stable: true, updated_at: ~N[2018-09-19 10:58:49.220000]}])
        (inform_api) lib/inform_api_web/controllers/user_take_list_controller.ex:37: InformAPIWeb.User_take_listController.index/2
        (inform_api) lib/inform_api_web/controllers/user_take_list_controller.ex:1: InformAPIWeb.User_take_listController.action/2
        (inform_api) lib/inform_api_web/controllers/user_take_list_controller.ex:1: InformAPIWeb.User_take_listController.phoenix_controller_pipeline/2
        (inform_api) lib/inform_api_web/endpoint.ex:1: InformAPIWeb.Endpoint.instrument/4
        (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.__call__/1
        (inform_api) lib/inform_api_web/endpoint.ex:1: InformAPIWeb.Endpoint.plug_builder_call/2
        (inform_api) lib/plug/debugger.ex:122: InformAPIWeb.Endpoint."call (overridable 3)"/2
        (inform_api) lib/inform_api_web/endpoint.ex:1: InformAPIWeb.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:16: Plug.Adapters.Cowboy.Handler.upgrade/4
        (cowboy) c:/Users/dbradley/Projects/inform-api/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

根据 decoratex 的文档,Decoratex.decorate/1 接受单个项目,而不是列表。

改变

Repo.all(query) |> Decoratex.decorate

Repo.all(query) |> Enum.map(&Decoratex.decorate/1)

应该可以。