通过主键以外的字段查找记录

Looking up records by a field other than primary key

在 Ecto 中我可以轻松做到这一点:

Repo.get!(Kaderi.Forum, params["id"])

通过主键查找记录,默认为id

不过,我正在考虑使用 slug 而不是 ID 来实现漂亮的 URL。我的模型中有一个 slug 字段,我可以很容易地使用它在 Phoenix 中生成 URL,如下所示:

defmodule Kaderi.Forum do
  use Kaderi.Web, :model

  @derive {Phoenix.Param, key: :slug}
  ...

但是似乎没有一种简单的方法可以通过 slug 字段自动查找记录。

可以做如下事情:

Repo.get_by!(Kaderi.Forum, slug: params["id"])

但似乎应该有一些在模型中配置它的好方法,这样我就可以自动生成 URL,然后通过 slugs 查找记录,而无需触摸控制器。如果我以后改变生成漂亮 URL 的方式,我就不必再去更新控制器了。

我是否缺少一些巧妙的 Ecto/Phoenix 技巧来轻松做到这一点?

如果您的 slug 包含 id(如 3-my-page 而不仅仅是 my-page,则可以这样做)

这是为方便起见从 Ecto.Type 文档中复制的示例:

defmodule Permalink do
  @behaviour Ecto.Type
  def type, do: :integer

  # Provide our own casting rules.
  def cast(string) when is_binary(string) do
    case Integer.parse(string) do
      {int, _} -> {:ok, int}
      :error   -> :error
    end
  end

  # We should still accept integers
  def cast(integer) when is_integer(integer), do: {:ok, integer}

  # Everything else is a failure though
  def cast(_), do: :error

  # When loading data from the database, we are guaranteed to
  # receive an integer (as databases are strict) and we will
  # just return it to be stored in the model struct.
  def load(integer) when is_integer(integer), do: {:ok, integer}

  # When dumping data to the database, we *expect* an integer
  # but any value could be inserted into the struct, so we need
  # guard against them.
  def dump(integer) when is_integer(integer), do: {:ok, integer}
  def dump(_), do: :error
end

然后您可以覆盖架构的主键:

defmodule Post do
  use Ecto.Schema

  @primary_key {:id, Permalink, autogenerate: true}
  schema "posts" do
    ...
  end
end

现在,当您调用 Repo.get(Page, "3-my-page") 时,字符串“3-my-page”将被转换为整数 3(这是模型的主键)并且页面将被返回。

如果你的 slug 中没有整数那么目前没有简单的方法来做到这一点,你最好继续使用 Repo.get_by.