first_or_create 等效于外

first_or_create equivalent in ecto

设置

具有此资源的 Phoenix 1.4.11 应用程序:

mix phx.gen.html Institutions SchoolType school_types name

问题

我想找到具有给定名称的第一个 school_type。如果那不存在,我想创建它。在 ActiveRecord 中我会使用 first_or_create.

我有这个有效的代码。但它看起来不太好,可能有更好更清洁的方法来解决这个问题。

school_type_name = "Example"

if school_type_name != nil do
  query =
    from s in SchoolType,
      where: s.name == ^school_type_name,
      limit: 1

  case Repo.one(query) do
    nil ->
      {:ok, _school_type} =
        Institutions.create_school_type(%{
          name: school_type_name
        })

    _ ->
      nil
  end

  school_type = Repo.one(query)
end

我该如何解决这个清洁剂问题?

正如您从源代码中看到的那样,如果还没有记录,first_or_create 会对数据库执行两次查询

# File activerecord/lib/active_record/relation.rb, line 103
def first_or_create(attributes = nil, &block) # :nodoc:
  first || create(attributes, &block)
end

要模仿这种行为,您应该避免在记录已存在的情况下执行两次查询。

school_type =
  case Repo.one(query) do
    nil ->
      {:ok, new} =
        Institutions.create_school_type(%{
          name: school_type_name
        })
      new

    found -> found # it’s already there!
  end