Elixir 向EXTO模型中添加一个随机且唯一的字段

Elixir 向EXTO模型中添加一个随机且唯一的字段,elixir,phoenix-framework,ecto,Elixir,Phoenix Framework,Ecto,我希望在外太空模型中有一个独特的领域。此字段应包含一个我可以轻松生成的随机字符串(例如,请参阅)。但是,我希望避免生成字符串并检查它是否已经存在于数据库中,因为这会使我暴露于竞争条件 我想让它重试插入,直到找到唯一的字符串。但是我该怎么做呢?它是否应该在变更集/2功能中 defmodule LetsPlan.Event do 使用LetsPlan.Web,:model 模式“事件”做什么 字段:名称,:字符串 字段:from,exto.DateTime 字段:to,exto.DateTime 字

我希望在外太空模型中有一个独特的领域。此字段应包含一个我可以轻松生成的随机字符串(例如,请参阅)。但是,我希望避免生成字符串并检查它是否已经存在于数据库中,因为这会使我暴露于竞争条件

我想让它重试插入,直到找到唯一的字符串。但是我该怎么做呢?它是否应该在
变更集/2
功能中

defmodule LetsPlan.Event do
使用LetsPlan.Web,:model
模式“事件”做什么
字段:名称,:字符串
字段:from,exto.DateTime
字段:to,exto.DateTime
字段:slug,:string
时间戳
结束
@必填字段~w(从到)
@可选_字段~w(段塞)
def变更集(模型,参数\\:空)do
模型
|>强制转换(参数、@必填字段、@可选字段)
|>唯一_约束(:slug)
结束
结束

根据@tkowal的建议,我写了以下内容。在模型模块中:

def变更集(模型,参数\\:空)do
除非参数==:空do
params=params |>cast_date(“from”)|>cast_date(“to”)
结束
模型
|>强制转换(参数、@必填字段、@可选字段)
|>唯一_约束(:slug)
结束
defp铸造日期(参数、键)do
参数|>Map.update(key、nil和Utils.to_-exto_-date/1)
结束
在控制器中:

def create(conn,%{“event”=>params})do
params=Map.put(params,“slug”,Utils.random_字符串(10))
changeset=Event.changeset(%Event{},params)
案例回购插入(变更集)do
{:好的,事件}->
康涅狄格州
|>put_flash(:info,“事件创建成功”)
|>重定向(到:事件路径(连接、显示、事件段塞))
{:错误,变更集}->
如果关键字.has_key?changeset.errors,:slug do
创建(conn,%{“event”=>params})
其他的
render conn,“new.html”,变更集:变更集
结束
结束
结束

欢迎各种反馈

已经4个月了,我想你已经明白了。您应该根据正在执行的操作创建不同的变更集,并为“读取”目的创建一个基本变更集

显性>隐性

你的模型可能会这样结束:

defmodule App.do
@规则创建%{
:必填\u字段=>~w(租户\u id姓氏电子邮件密码\u确认电话生日说明),
:可选_字段=>~w(),
}
@规则更新%{
:必填_字段=>~w(名字姓氏电子邮件电话生日描述),
:可选_字段=>~w()
}
def变更集(模型,参数\\:空)do
模型
|>强制转换(参数,[],[])
结束
@“医生”
创建新分类时的变更集
"""
def create_变更集(模型,参数\\:空)do
模型
|>强制转换(参数、@rules\u create.required\u字段、@rules\u create.optional\u字段)
|>验证长度(:说明,最小值:280)
|>验证长度(:密码,最小值:6)
|>验证确认(:密码)
|>唯一_约束(:电子邮件)
|>散列密码
|>代币
|>搜索
结束
@“医生”
更新分类时的变更集
"""
def update_变更集(模型,参数\\:空)do
模型
|>强制转换(参数、@rules\u update.required\u字段、@rules\u update.optional\u字段)
|>验证长度(:说明,最小值:280)
|>搜索
结束
结束

请注意,
unsafe\u validate\u unique
不能保证它是唯一的,尽管是由于赛车条件。但是应该在99%的情况下对您有效。

是的,在
变更集
函数中有约束,当其他字段准备就绪时,在控制器中生成slug,将其放入变更集中并尝试保存。然后在三种情况下进行匹配a)有效->继续b)更改集。关于slug的错误->递归调用自身生成slug,然后重试c)其他错误->处理或出现在GUI中。@tkowal好的,我知道了,但我有一个问题:如何区分错误?例如,我如何知道插入失败是因为slug还是因为有其他错误?@tkowal Nevermind。我阅读了Ecto的源代码,发现错误被放在
changeset.error
中。“你这么说了,但我以前不明白。”杰里米·盖斯:用你的方法,我想你可能会面临比赛条件。检查此链接
defmodule App.User.Slug do

  import Ecto.Changeset, only: [unsafe_validate_unique: 3, change: 2]

  def build_slug(changeset) do
    slug = your_fn_to_build_slug(changeset.username)  
    make_sure_unique(slug)
  end

  defp make_sure_unique(slug, attempt \\ 1) do
    slug = if attempt > 1, do: "#{slug}-#{attempt}", else: slug
    changeset = change(%User{}, slug: slug)
    changeset = unsafe_validate_unique(changeset, [:slug], App.Repo)

    if is_slug_unique(changeset) do
      slug
    else
      make_sure_unique(slug, attempt + 1)
    end
  end

  defp is_slug_unique(%Ecto.Changeset{valid?: true}), do: true
  defp is_slug_unique(_), do: false
end

model
  |> cast(params, @required_fields, @optional_fields)
  |> App.User.Slug.build_slug
  |> other_validations_you_need