2016-06-10 5 views
6

Ich bin wirklich neu in Phoenix/Elixir und ich versuche, Änderungen zu ändern.Wie wird ein Ecto-Änderungssatz vor dem Einfügen in den Repo geändert?

Ich verstehe, dass es eine Reihe von Änderungen enthält, die entweder zum Erstellen oder Aktualisieren eines Modells verwendet werden.

Was ich gerne wissen würde ist, ob und wie ich eine Änderung ändern kann, bevor Sie es in die Datenbank schieben.

Mein Anwendungsfall ist die folgende:

  • Ich habe eine Form, die Menschen ermöglichen, neue Künstler in der Datenbank zu erstellen.
  • In dieser Form gibt es ein Fachgebiet.
  • Bevor der Künstler zu schaffen, möchte ich durch das Spezialgebiet spalten „“ es zu speichern, als ein Array von String

Ich bin nicht einmal sicher, dass es durch Änderung direkt auf die changeset aufgrund Unveränderlichkeit Einschränkungen machbar aber ich könnte vielleicht ein anderes Changeset erstellen, um es in den Repo einzufügen.

Jeder Vorschlag ist willkommen und zögere nicht, auf schlechte Praktiken oder dumme Dinge hinzuweisen, die ich machen könnte!

EDIT folgenden Kommentar: Ich freue mich auf so etwas wie:

defp put_specialty_array(changeset) do 
    case changeset do 
    %Ecto.Changeset{valid?: true, changes: %{specialty: spec}} -> 
     put_change(changeset, :specialty, String.split(spec, ",")) 
    _ -> 
     changeset 
    end 
end 
+0

Dies ist ein ähnlicher Ansatz zum Hashing und Speichern eines Benutzerkennworts. Sehen Sie nach, wie ["Phoenix programmieren"] (https://pragprog.com/book/phoenix/programming-phoenix) es [hier] gemacht hat (https://media.pragprog.com/titles/phoenix/code/authentication/listings) /rumbl/web/models/user.change1.ex). (Genauer gesagt, wie 'registration_changeset' ruft' put_pass_hash') – AbM

+0

So konnte ich so etwas wie: defp put_specialty_array (changeset) tun Fall ChangeSet % Ecto.Changeset tun {gültig ?: wahr, Änderungen:% {Spezialität: spec }} -> put_change (Changeset,: Spezialität, String.split (spec, ",")) changeset Ende Ende ? – Cratein

+0

Korrigiert Ihren Vorschlag ein wenig – AbM

Antwort

9

Ich glaube, für das, was Sie suchen eine benutzerdefinierte Ecto.Type ist. Ich mache das die ganze Zeit, und es funktioniert super! Es würde wie folgt aussehen:

defmodule MyApp.Tags do 
    @behaviour Ecto.Type 
    def type, do: {:array, :string} 
    def cast(nil), do: {:ok, nil} # if nil is valid to you 
    def cast(arr) when is_list(arr) do 
    if Enum.all?(arr, &String.valid?/1), do: {:ok, arr}, else: :error 
    end 
    def cast(str) when is_binary(str), do: {:ok, String.split(",")} 
    def cast(_), do: :error 

    def dump(val) when is_list(val), do: {:ok, val} 
    def dump(_), do: :error 
    def load(val) when is_list(val), do: {:ok, val} 
    def load(_), do: :error 
end 

Dann in der Migration, fügen Sie eine Spalte mit der richtigen Art

add :tags, {:array, :string} 

schließlich in Ihrem Schema den Feldtyp angeben, die Sie erstellt haben.

field :tags, MyApp.Tags 

Dann können Sie es einfach als ein Feld in Ihrem Changeset hinzufügen. Wenn die Umwandlung Ihres Typs :error zurückgibt, wird das Änderungsset einen Fehler wie {:tags, ["is invalid"]} haben. Sie müssen sich dann keine Gedanken über die Verarbeitung des Feldes in Ihrem Modell oder Controller machen. Wenn der Benutzer ein Zeichenfolgenarray für den Wert oder nur eine durch Kommas getrennte Zeichenfolge sendet, funktioniert es.

Wenn Sie den Wert in die Datenbank in einem anderen Format speichern müssen, würden Sie nur den Rückgabewert von def type ändern und sicherstellen, dass def dump einen Wert dieses Typs zurückgibt und def load einen Wert dieses Typs lesen, um was auch immer interne Darstellung, die Sie wollen. Ein übliches Muster ist, eine Struktur für die interne Darstellung zu definieren, so dass Sie Ihre eigene Implementierung von Poisons to_json machen können, die sogar eine einfache Zeichenkette zurückgeben könnte. Ein Beispiel könnte ein LatLng-Typ sein, der in JSON nach 12.12345N,123.12345W kodiert, in Postgres als GIS-Typ gespeichert wird, aber über eine Struktur wie %LatLng{lat: 12.12345, lng: -123.12345} verfügt, mit der man in Elixir einige einfache Berechnungen anstellen kann. Die DateTime-Formate funktionieren ähnlich (es gibt eine Struktur für Elixir, ein Tupel-Format für den DB-Treiber und ein ISO-Format für JSON).

Ich denke, das funktioniert wirklich gut für Passwortfelder, BTW.Sie können die JSON-Darstellung zerquetschen, eine Struktur verwenden, um den Algorithmus darzustellen, Parameter für den Algorithmus getrennt von Hash oder was auch immer das Leben einfacher macht. In Ihrem Code, um ein Passwort zu aktualisieren, wäre es nur Ecto.Changeset.change(user, password: "changeme").

Ich weiß, das ist eine 6mo alte Frage, und Sie haben wahrscheinlich etwas gefunden, aber ich bin hier von einer Google-Suche gelandet, und nehme an, andere werden auch.

+0

Wissen Sie, ob es eine Möglichkeit gibt, die kontextabhängigen Daten des aktuellen Benutzers in das benutzerdefinierte Feld zu injizieren? Sagen Sie in dem Fall, in dem ich das Feld mit dem Passphrase-Schlüssel des Benutzers verschlüsseln/entschlüsseln möchte und den Schlüssel aus dem benutzerdefinierten Feld referenzieren muss. –

Verwandte Themen