2017-09-20 1 views
1

Ich habe Xyz, die entweder eine country_id, eine federal_state_id oder eine city_id hat. Aber nur einer von ihnen (nicht alle drei und auch nicht zwei). Wie kann ich eine Validierung dafür durchführen? Und wie kann ich ein assoc_constraint/1 für dieses Feld nur für dieses tun?validate_required für eins von drei

defmodule Example.Location.Xyz do 
    use Ecto.Schema 
    import Ecto.Changeset 
    alias Example.Location.Xyz 

    schema "xyzs" do 
    field :name, :string 
    belongs_to :country, Example.Location.Country 
    belongs_to :federal_state, Example.Location.FederalState 
    belongs_to :city, Example.Location.City 

    timestamps() 
    end 

    @doc false 
    def changeset(%Xyz{} = xyz, attrs) do 
    school 
    |> cast(attrs, [:name, :country_id, :federal_state_id, :city_id]) 
    |> validate_required([:name, :country_id, :federal_state_id, :city_id]) 
    |> assoc_constraint(:country) 
    |> assoc_constraint(:federal_state) 
    |> assoc_constraint(:city) 
    end 
end 

Antwort

3

ich eine Funktion erstellen würde, die genau 1 der 3 Felder vorhanden ist, und fügen Sie auch das Recht prüft, ob assoc_constraint: me

@doc false 
def changeset(%Xyz{} = xyz, attrs) do 
    school 
    |> cast(attrs, [:name, :country_id, :federal_state_id, :city_id]) 
    |> validate_required([:name]) 
    |> validate_one_of_present([:country_id, :federal_state_id, :city_id]) 
end 

def validate_one_of_present(changeset, fields) do 
    fields 
    |> Enum.filter(fn field -> 
    # Checks if a field is "present". 
    # The logic is copied from `validate_required` in Ecto. 
    case get_field(changeset, field) do 
     nil -> false 
     binary when is_binary(binary) -> String.trim_leading(binary) == "" 
     _ -> true 
    end 
    end) 
    |> case do 
    # Exactly one field was present. 
    [field] -> 
     without_id = field |> Atom.to_string |> String.replace_suffix("_id", "") |> String.to_existing_atom 
     assoc_constraint(changeset, without_id) 
    # Zero or more than one fields were present. 
    _ -> 
     add_error(changeset, hd(fields), "expected exactly one of #{inspect(fields)} to be present") 
    end 
end 

-Code ist nicht getestet, lassen Sie wissen, ob Sie finden Fehler!

+2

Wow, wusste nicht, dass Sie in eine case-Anweisung pipen können. Aber es macht definitiv Sinn. – Phillipp

Verwandte Themen