2016-06-10 43 views
6

Jestem naprawdę nowy w Phoenix/Elixir i staram się owijać głowę wokół zestawów zmian.Jak zmodyfikować zestaw zmian Ecto przed wstawieniem go do repo?

Rozumiem, że zawiera zestaw zmian używanych do tworzenia lub aktualizowania modelu.

Chciałbym się dowiedzieć, czy i jak mogę zmodyfikować zmianę przed przekazaniem jej do bazy danych.

Moje przypadek użycia jest następujący:

  • Mam formularz, który umożliwi użytkownikom tworzenie nowych artystów w bazie.
  • W tym formularzu znajduje się pole specjalizacji.
  • Przed utworzeniem artystę, chcę podzielić pole specjalne za „” aby zapisać go jako tablica ciąg

Nie jestem nawet pewien, że to wykonalne modyfikując bezpośrednio changeset powodu ograniczeń niezmienność ale mógłbym stworzyć inny zestaw zmian, który można wstawić do repozytorium.

Wszelkie sugestie są mile widziane i nie wahaj się wskazać złych praktyk lub głupich rzeczy, które robię!

EDIT następujący komentarz: Patrzę na coś takiego:

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

Jest to podobne podejście do haszowania i przechowywania hasła użytkownika. Sprawdź, jak ["Programowanie Phoenix"] (https://pragprog.com/book/phoenix/programming-phoenix) zrobił to [tutaj] (https://media.pragprog.com/titles/phoenix/code/authentication/listings /rumbl/web/models/user.change1.ex). (Konkretnie jak 'registration_changeset' dzwoni' put_pass_hash') – AbM

+0

abym mógł zrobić coś takiego: defp put_specialty_array (changeset) zrobić sprawę changeset zrobić % Ecto.Changeset {poprawne ?: prawdziwe zmiany:% {specjalność: specyfikacji }} -> put_change (zestaw zmian,: specjalizacja, String.split (spec, ",")) zestaw zmian koniec koniec ? – Cratein

+0

Naprawiono sugestię nieco – AbM

Odpowiedz

9

Wierzę, czego szukasz jest zwyczaj Ecto.Type. Robię to cały czas i działa świetnie! Byłoby to wyglądać mniej więcej tak:

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 

Następnie w swojej migracji, dodaj kolumnę z prawej Typ

add :tags, {:array, :string} 

Wreszcie w schemacie określić typ pola, który został utworzony.

field :tags, MyApp.Tags 

Następnie możesz po prostu dodać go jako pole w swoim zestawie zmian. Jeśli odlew Twojego typu zwróci :error, to zestaw zmian będzie miał błąd podobny do {:tags, ["is invalid"]}. Nie musisz się wtedy martwić o jakiekolwiek przetwarzanie pola w swoim modelu lub kontrolerze. Jeśli użytkownik napisze tablicę ciągów dla wartości lub po prostu ciąg znaków oddzielonych przecinkami, będzie działał.

Jeśli trzeba zapisać wartość do bazy danych w innym formacie, należy po prostu zmienić wartość zwracaną def type i upewnić się, że def dump zwraca wartość tego rodzaju i że def load można odczytać wartość tego typu na cokolwiek reprezentacja wewnętrzna, którą chcesz. Jednym wspólnym schematem jest zdefiniowanie struktury wewnętrznej reprezentacji, aby można było wprowadzić własną implementację Poison's to_json, która mogłaby nawet zwrócić prosty ciąg znaków. Jednym z przykładów może być typ LatLng, który koduje do 12.12345N,123.12345W w jsonie, jako niektóre typy GIS w postgresie, ale ma strukturę podobną do %LatLng{lat: 12.12345, lng: -123.12345}, która pozwala ci wykonać prostą matematykę w eliksiru. Formaty DateTime działają bardzo podobnie (jest tam struktura dla eliksiru, format tupu dla sterownika db i format ISO dla json).

Myślę, że to działa bardzo dobrze dla pól hasła, btw.Możesz zgnieść reprezentację JSON, użyć struktury do reprezentowania algorytmu, parametry do algorytmu oddzielić sól od hash lub cokolwiek innego ułatwia życie. W twoim kodzie, aby zaktualizować hasło, będzie to po prostu Ecto.Changeset.change(user, password: "changeme").

Zdaję sobie sprawę, że jest to stare pytanie o długości 6 milionów i prawdopodobnie coś znalazłeś, ale znalazłem się tutaj z wyszukiwarki Google i zakładam, że inni też.

+0

Czy wiesz, czy istnieje sposób na wprowadzenie kontekstowych danych bieżącego użytkownika do pola niestandardowego? Powiedz w przypadku, w którym chcę zaszyfrować/odszyfrować pole przy użyciu klucza hasła użytkownika i potrzebować odwołania do klucza w niestandardowym polu. –