phoenixframework 中的 Ecto 动态查询参数
Ecto dynamic query params in phoenixframework
我是 Elixir 和 Phoenix 框架的新手。如何动态地将参数添加到 ecto 查询?例如:
def index(conn, _params) do
departments = Repo.all(Department)
render(conn, "index.json", data: departments)
end
是否可以在 Ecto 查询中使用 _params ?类似于:
Repo.get_by(Department, _params)
您当然可以动态获取内容,第二个参数中的 Repo.get_by 需要 Keyword.t | Map.t
,关键字或映射。您可以将其直接从参数设置为地图或关键字,然后将其传递到查询中。
例子
def index(conn, _params) do
query_conditions = [id: params["id"], name: params["department_name"] ]
departments = Repo.get_by(Department, query_conditions)
render(conn, "index.json", data: departments)
end
正如@Dogbert 在评论中所述,get_by
将得到 0 或 1 个结果,更多结果将引发错误。我之所以使用它,是因为您在问题中使用了 get_by
。
您可以使用 "bindingless queries" for this, but there's a problem. Ecto expects the column names to be atoms and the argument to be a keyword list. The latter is easy, but 。这是使用列名白名单执行此操作的安全方法:
# user input
params = %{"first_name" => "John", "last_name" => "Smith", "age" => 28}
filtered_params =
params
|> Map.take(~w(first_name last_name age))
|> Enum.map(fn {k, v} -> {String.to_atom(k), v} end)
from(MyApp.Person, where: ^filtered_params)
|> MyApp.Repo.all
|> IO.inspect
输出:
[debug] SELECT p0."id", p0."first_name", p0."last_name", p0."age", p0."inserted_at", p0."updated_at" FROM "people" AS p0 WHERE (((p0."age" = ?) AND (p0."first_name" = ?)) AND (p0."last_name" = ?)) [28, "John", "Smith"] OK query=6.5ms queue=14.8ms
[%MyApp.Person{__meta__: #Ecto.Schema.Metadata<:loaded>, age: 28,
first_name: "John", id: 1, inserted_at: #Ecto.DateTime<2016-06-11T16:01:49Z>,
last_name: "Smith", updated_at: #Ecto.DateTime<2016-06-11T16:01:49Z>}]
我知道这个帖子很旧,但也可以利用 Ecto.Changeset.cast
,并从其验证中获益。例如,要 select all:
with %Ecto.Changeset{valid?: true} = changeset <- Ecto.Changeset.cast(%Department{}, params, [:department_name]) do
clauses = Enum.to_list(changeset.changes)
query =
from e in Department,
where: ^clauses,
order_by: [desc: e.inserted_at]
{:ok, Repo.all(query)}
else
changeset -> {:error, changeset}
end
我是 Elixir 和 Phoenix 框架的新手。如何动态地将参数添加到 ecto 查询?例如:
def index(conn, _params) do
departments = Repo.all(Department)
render(conn, "index.json", data: departments)
end
是否可以在 Ecto 查询中使用 _params ?类似于:
Repo.get_by(Department, _params)
您当然可以动态获取内容,第二个参数中的 Repo.get_by 需要 Keyword.t | Map.t
,关键字或映射。您可以将其直接从参数设置为地图或关键字,然后将其传递到查询中。
例子
def index(conn, _params) do
query_conditions = [id: params["id"], name: params["department_name"] ]
departments = Repo.get_by(Department, query_conditions)
render(conn, "index.json", data: departments)
end
正如@Dogbert 在评论中所述,get_by
将得到 0 或 1 个结果,更多结果将引发错误。我之所以使用它,是因为您在问题中使用了 get_by
。
您可以使用 "bindingless queries" for this, but there's a problem. Ecto expects the column names to be atoms and the argument to be a keyword list. The latter is easy, but
# user input
params = %{"first_name" => "John", "last_name" => "Smith", "age" => 28}
filtered_params =
params
|> Map.take(~w(first_name last_name age))
|> Enum.map(fn {k, v} -> {String.to_atom(k), v} end)
from(MyApp.Person, where: ^filtered_params)
|> MyApp.Repo.all
|> IO.inspect
输出:
[debug] SELECT p0."id", p0."first_name", p0."last_name", p0."age", p0."inserted_at", p0."updated_at" FROM "people" AS p0 WHERE (((p0."age" = ?) AND (p0."first_name" = ?)) AND (p0."last_name" = ?)) [28, "John", "Smith"] OK query=6.5ms queue=14.8ms
[%MyApp.Person{__meta__: #Ecto.Schema.Metadata<:loaded>, age: 28,
first_name: "John", id: 1, inserted_at: #Ecto.DateTime<2016-06-11T16:01:49Z>,
last_name: "Smith", updated_at: #Ecto.DateTime<2016-06-11T16:01:49Z>}]
我知道这个帖子很旧,但也可以利用 Ecto.Changeset.cast
,并从其验证中获益。例如,要 select all:
with %Ecto.Changeset{valid?: true} = changeset <- Ecto.Changeset.cast(%Department{}, params, [:department_name]) do
clauses = Enum.to_list(changeset.changes)
query =
from e in Department,
where: ^clauses,
order_by: [desc: e.inserted_at]
{:ok, Repo.all(query)}
else
changeset -> {:error, changeset}
end