2016-03-21 7 views
1

Ich möchte ein eindeutiges Feld in einem Ecto-Modell haben. Dieses Feld sollte eine Zufallszeichenkette enthalten, die ich leicht erzeugen kann (siehe zB here). Allerdings möchte ich es vermeiden, die Zeichenkette zu generieren und zu überprüfen, ob sie bereits in der Datenbank vorhanden ist, da dies mich den Rennbedingungen aussetzen würde.Hinzufügen eines zufälligen und eindeutigen Felds zu Ecto-Modell

Ich möchte es Insertion wiederholen versuchen, bis eine eindeutige Zeichenfolge gefunden wird. Aber wie mache ich es? Sollte es in der changeset/2 Funktion sein?

defmodule LetsPlan.Event do 
    use LetsPlan.Web, :model 

    schema "events" do 
    field :name, :string 
    field :from, Ecto.DateTime 
    field :to, Ecto.DateTime 
    field :slug, :string 

    timestamps 
    end 

    @required_fields ~w(from to) 
    @optional_fields ~w(slug) 

    def changeset(model, params \\ :empty) do 
    model 
    |> cast(params, @required_fields, @optional_fields) 
    |> unique_constraint(:slug) 
    end 
end 
+0

Ja, haben die Einschränkung in 'changeset' Funktion und in dem Controller, wenn andere Felder sind bereit, erzeugen die Schnecke , setze es in den Änderungssatz und versuche es zu speichern. Dann auf drei Fälle übereinstimmen a) es hat funktioniert -> weiter b) changeset.error über slug -> rekursiv sich selbst aufrufen, um slug zu generieren und es erneut zu versuchen c) andere Fehler -> handle oder in GUI präsentieren. – tkowal

+0

@tkowal Ok, ich habe es aber ich habe eine Frage: Wie unterscheide ich zwischen Fehlern? Zum Beispiel, woher weiß ich, dass die Einfügung wegen des Slugs fehlgeschlagen ist oder weil andere Fehler aufgetreten sind? – rubik

+1

@tkowal Nevermind. Ich habe Ectos Quellcode gelesen und festgestellt, dass Fehler in 'changeset.error' stehen. Du hast es gesagt, aber ich habe es vorher nicht verstanden. – rubik

Antwort

2

Es ist 4 Monate her, also haben Sie es herausgefunden. Sie sollten abhängig von der Aktion, die Sie gerade ausführen, einen anderen Änderungssatz und einen Basisänderungssatz für "Lese" -Zwecke erstellen.

Explicit> Implizite

Ihr Modell wie das könnte am Ende:

defmodule App.Classified do 

    @rules_create %{ 
    :required_fields => ~w(tenant_id firstname lastname email password password_confirmation phone birthday description), 
    :optional_fields => ~w(), 
    } 

    @rules_update %{ 
    :required_fields => ~w(firstname lastname email phone birthday description), 
    :optional_fields => ~w() 
    } 

    def changeset(model, params \\ :empty) do 
    model 
    |> cast(params, [], []) 
    end 

    @doc """ 
    Changeset when you create a new classified 
    """ 
    def create_changeset(model, params \\ :empty) do 
    model 
    |> cast(params, @rules_create.required_fields, @rules_create.optional_fields) 
    |> validate_length(:description, min: 280) 
    |> validate_length(:password, min: 6) 
    |> validate_confirmation(:password) 
    |> unique_constraint(:email) 
    |> hash_password 
    |> make_token 
    |> make_search 
    end 

    @doc """ 
    Changeset when you update an classified 
    """ 
    def update_changeset(model, params \\ :empty) do 
    model 
     |> cast(params, @rules_update.required_fields, @rules_update.optional_fields) 
     |> validate_length(:description, min: 280) 
     |> make_search 
    end 

end 
+0

Schön! Es ist in der Tat viel besser lesbar. – rubik

0

Nach @tkowal Vorschlag, schrieb ich folgendes. Im Modellmodul:

def changeset(model, params \\ :empty) do 
    unless params == :empty do 
    params = params |> cast_date("from") |> cast_date("to") 
    end 

    model 
    |> cast(params, @required_fields, @optional_fields) 
    |> unique_constraint(:slug) 
end 

defp cast_date(params, key) do 
    params |> Map.update(key, nil, &Utils.to_ecto_date/1) 
end 

In der Steuerung:

def create(conn, %{"event" => params}) do 
    params = Map.put(params, "slug", Utils.random_string(10)) 
    changeset = Event.changeset(%Event{}, params) 

    case Repo.insert(changeset) do 
    {:ok, event} -> 
     conn 
     |> put_flash(:info, "Event created successfully") 
     |> redirect(to: event_path(conn, :show, event.slug)) 
    {:error, changeset} -> 
     if Keyword.has_key? changeset.errors, :slug do 
     create(conn, %{"event" => params}) 
     else 
     render conn, "new.html", changeset: changeset 
     end 
    end 
end 

Alle Art von Feedback ist willkommen!