2015-08-25 18 views
8

Gram z Elixirem i frameworkiem internetowym Phoenix, ale teraz utknąłem na próbie sprawdzenia ograniczenia klucza obcego. Tak więc, biorąc pod uwagę model Post z wielu komentarzach pisałem model Comment następująco:Elixir Ecto: Jak sprawdzić poprawność klucza obcego?

defmodule MyApp.Comment do 
    use MyAPp.Web, :model 

    schema "comments" do 
    field :body, :text 
    belongs_to :post, MyApp.Post 

    timestamps 
    end 

    @required_fields ~w(body post_id) 
    @optional_fields ~w() 

    def changeset(model, params \\ :empty) do 
    model 
    |> cast(params, @required_fields, @optional_fields) 
    |> foreign_key_constraint(:post_id) 
    end 
end 

a jego testy jednostkowe:

defmodule MyApp.CommentTest do 
    # [...] 
    test "changeset with non existent post" do 
    attrs = %{ 
     body: "A comment." 
     post_id: -1 # some non-existent id? 
    } 
    refute Comment.changeset(%Comment{}, attrs).valid? 
    assert {:post_id, "does not exist"} in errors_on(%Comment{}, %{}) 
    end 
end 

Według http://hexdocs.pm/ecto/Ecto.Changeset.html#foreign_key_constraint/3:

Ograniczenie klucza obcego działa, opierając się na bazie danych, aby sprawdzić , jeśli powiązany model istnieje. Jest to przydatne, aby zagwarantować, że dziecko zostanie utworzone tylko wtedy, gdy obiekt nadrzędny istnieje również w bazie danych .

Spodziewałem się, że napisany przeze mnie kod zadziałał, zamiast tego sprawdza tylko obecność (zgodnie z definicją w @required_fields ~w(body post_id)). Nie wykluczam, że zrobiłem coś złego lub źle zrozumiałem oświadczenie w dokumentach.

Czy ktoś już się o to potknął?

UPDATE: Dla kompletności, tutaj jest migracja:

def change do 
    create table(:comments) do 
    add :body, :text 
    add :post_id, references(:posts) 

    timestamps 
    end 

    create index(:comments, [:post_id]) 
end 
+2

Czy możesz podać swoją migrację? – Gazler

+0

@Gazler zredagował moje pytanie, dodał migrację. –

Odpowiedz

9

Ponieważ jest to zależne od bazy danych, należy dodać referencje do migracji i wykonać rzeczywistą operację bazy danych. Musisz zadzwonić pod numer Repo.insert/1 lub Repo.update/1, podając swój zestaw zmian, a następnie zwróci on {:error, changeset}.

Pamiętaj, że nie ma żadnych przedmiotów w eliksiru ani w Ecto. Dlatego też changeset.valid? nigdy nie może wykonać operacji bazy danych, jest to po prostu dane odzwierciedlające zestaw zmian, które należy wykonać, a stan tych danych zmienia się podczas wykonywania operacji, takich jak wstawianie lub aktualizacja.

Ostatnia uwaga, errors_on/2, zawsze zwróci nowy zestaw zmian, a nie ten, z którym dotychczas pracowałeś. Twoja ostatnia linia prawdopodobnie będzie:

assert {:post_id, "does not exist"} in changeset.errors 
+0

Bardzo dziękuję za odpowiedź. W tym przypadku problem był gdzie indziej: nie przetworzyłem testowej bazy danych po pewnych modyfikacjach migracji. Schemat był więc bez ograniczeń. Ponadto wprowadziłem zmiany, które zasugerowałeś. Jeszcze raz dziękuję! :-) –

1

„opierając się na bazie” oznacza, że ​​trzeba mieć klucz obcy wymuszenie w modelu danych.

W swojej migracji, powinien mieć coś takiego:

create table(:comments) do 
    add :post_id, references(:posts) 
end 

który wymusza klucz obcy Ograniczenie sprawdzające między rodzicem a tabeli podrzędnej.

+0

Rzeczywiście, zrobiłem to podczas mojej migracji. Zaktualizowałem moje pytanie i dodałem kod migracji. –

Powiązane problemy