2017-04-25 53 views
0

我生成了,像这样的模型的唯一密钥的唯一密钥...(Re)的生成通过唯一约束

def changeset(%__MODULE__{} = post, attrs) do 
    post 
    |> generate_key() 
    |> unique_constraint(:key) 
end 

defp generate_key(changeset) do 
    key = :crypto.strong_rand_bytes(5) 
    |> Base.url_encode64 
    |> binary_part(0, 5) 

    put_change(changeset, :key, key) 
end 

这可能不是这样做的最佳方式,可以随意提供其他建议,但我的问题是,在unique_constraint命中时重新生成密钥的最佳方式是什么?那么测试它的最好方法是什么?

编辑:这不完全是整个架构,它只是简化而已。我实际上有一个主要ID,以及其他一些领域。

我想要一个密钥,将公开分享给不同的用户,每个用户有不同的密钥,所以它必须是唯一的。它可以超过5个字符,虽然它会通过URL共享,所以我不想太长。还有一个更复杂的约束条件,它基于密钥和电子邮件地址验证唯一性,所以我不担心5的长度。无论长度如何,我希望在unique_constraint命中时重新生成该长度。

+0

我对这种语言一无所知,但它似乎你需要:https://github.com/zyro/elixir-uuid –

+0

它是否需要限制为5个字节?正如@NyamiouTheGaleanthrope所建议的那样,UUID /'binary_id'字段将通过拥有更大的密钥来避免冲突。 –

+0

我更新了这个问题,但是不需要将它限制为5,只是想让它对URL友好。尽管无论长度如何,如果与唯一性约束存在冲突,我希望重新生成。 UUID库看起来不错,但我在寻找比这更短的东西(类似于youtube的ID,我相信这是5个字符) – poops

回答

1

这里是我用来创建随机字符串

@spec random_string(integer) :: binary 
def random_string(length) do 
    length 
    |> :crypto.strong_rand_bytes 
    |> Base.url_encode64 
    |> binary_part(0, length) 
end 

与unique_constraint的问题是,直到写的DATEBASE尝试(Repo.insert或回购您的变更不会显示约束错误代码.update)

最简单的方法是使用单独的模块来处理插入。事情是这样的:

defmodule PostService do 
    def insert_post(params) do 
    changeset = Post.changeset(%Post{}, params) 
    case Repo.insert changeset do 
     {:error, %{errors: constraint_match}} -> 
     # constraint_error is just a placeholder for the correct match 
     insert_post(params) 
     error_or_ok -> 
     error_or_ok 
    end 
    end 
end 

但是,如果你使用一个UUID,我不认为你需要担心的碰撞。

+0

感谢您的回答,但我更新了我的问题,使其更清楚一点正在寻找。我已经有一个自动生成的主ID,除非您认为有2个自动生成的ID是可以的?否则,如果unique_constraint命中,是否有办法让模型自动重新运行您的第一个代码块? – poops

+0

明白了。我会更新我的答案。 –

+0

我也认为你可以添加第二个自动生成的binary_id,它将是唯一的。我从来没有尝试过。 –