2017-04-25 76 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,它將是唯一的。我從來沒有嘗試過。 –