模式和模型之间有什么区别?

时间:2020-04-28 23:47:23

标签: elixir phoenix-framework

我刚开始吃Phoenix,我对一件事不太了解,我搜索了一下,发现模式是数据库结构,模型具有较高的层次并可以处理逻辑,但是在phoenix中,我们只有模式,所以例如,如果我要进行密码哈希处理,则应在用户模式中进行?还是应该将其放在控制器中?

1 个答案:

答案 0 :(得分:1)

通常来说,您希望MVC应用程序具有“薄控制器”-这是一种建议,适用于多种语言。在某些语言/框架中,这很关键,因为测试控制器可能非常困难。尽管在功能语言(如Elixir)和结构良好的框架(如Phoenix)中测试控制器相对简单,但您仍应保持控制器尽可能精简。简而言之,控制器应将服务模块(在Phoenix中通常称为“上下文”)与视图连接。通常,其中根本没有很多逻辑。

“模式”可能是一个令人困惑的术语-花点时间了解不同的数据库使用不同的术语来指代(或多或少)相同的组件。例如。 MySQL中的“数据库”在Oracle或Postgres中称为“模式”。在Ecto中,“模式”类似于许多其他框架中的ORM“模型”:Ecto模式在代码中表示特定数据库表的“形状”(所以也许这就是为什么他们使用相同的术语)。

但是,在您的情况下,诸如密码哈希的计算字段之类的内容应与Ecto模式位于同一模块中。 (是的,Ecto在代码中定义表的形状时会执行一些宏操作,但它仍然是一个模块,建议在将changeset函数用于处理数据的验证和变异之前,将其放置在该位置。数据库)。

如果您想知道应该在哪里使用某些功能,请问一下自己,如果用户输入来自CLI而不是来自网络,则应将代码放在哪里。如果您有一个使用密码创建用户的CLI混合任务,您会使用Web控制器吗?不,您不会:控制器只是将Web请求/响应与基础模型/架构连接的中介。您不想只因为要为CLI脚本创建密码哈希而重复计算密码哈希的代码,这样就可以在逻辑上放置逻辑哈希:在架构中。

Ecto变更集允许您在进入数据库的过程中修改数据。您可能会在某些框架中将其称为“变异”或“计算字段”。

下面是一个计算“ created_at”字段的示例(是的,您可以在数据库中执行此操作,但这是一个如何在代码中执行计算字段的有用示例):

  @doc false
  def changeset(user, attrs) do
    user
    |> cast(attrs, [:username,:email])
    |> validate_required([:username,:email])
    |> add_created_at()
    |> unique_constraint(:username)
    |> unique_constraint(:email)
  end

  defp add_created_at(changeset) do
    case changeset do
      %Ecto.Changeset{
        valid?: true,
        changes: _user
      } ->
        put_change(
          changeset,
          :created_at,
          DateTime.utc_now
          |> DateTime.truncate(:second)
        )
      _ ->
        changeset
    end
  end

这是一个使用Argon2包计算要存储在数据库中的密码哈希的示例。这还会添加一个字段,该字段标识用于对密码进行哈希处理的算法(作为字符串,对参考很有用):

  def changeset(user, attrs) do
    user
    |> cast(attrs, [:username, :password])
    |> validate_required([:username, :password])
    |> validate_length(:password, min: 8, max: 100)
    |> unique_constraint(:username)
    |> put_password_hash()
  end


  defp put_password_hash(changeset) do
    case changeset do
      %Ecto.Changeset{
        valid?: true,
        changes: %{
          password: plain_text
        }
      } ->
        put_change(changeset, :password_hash, Argon2.hash_pwd_salt(plain_text))
        |> put_change(:algorithm, "argon2")

      _ ->
        changeset
    end
  end

希望有帮助。