2015-08-25 21 views
8

Ich spiele mit Elixir und dem Phoenix-Web-Framework, aber jetzt muss ich versuchen, eine Fremdschlüssel-Einschränkung zu validieren. Also, da ein Modell Post mit viele Kommentare, ich schrieb das Comment Modell wie folgt:Elixir Ecto: Wie validiert Fremdschlüssel Einschränkung?

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 

und seine Unit-Test:

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 

Nach http://hexdocs.pm/ecto/Ecto.Changeset.html#foreign_key_constraint/3:

Die Die Fremdschlüsseleinschränkung funktioniert, indem auf die Datenbank überprüft wird, wenn das zugehörige Modell existiert oder nicht. Dies ist nützlich, um zu garantieren, dass ein untergeordnetes Objekt nur dann erstellt wird, wenn das übergeordnete Objekt auch in der Datenbank existiert .

Ich habe erwartet, dass der Code, den ich geschrieben habe, funktioniert hat, stattdessen wird nur auf Präsenz überprüft (wie in @required_fields ~w(body post_id) definiert). Ich schließe nicht aus, ich habe etwas falsch gemacht oder die Aussage in den Dokumenten missverstanden.

Ist schon jemand darauf gestoßen?

UPDATE: Der Vollständigkeit halber ist hier die Migration:

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

    timestamps 
    end 

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

Könnten Sie bitte auch Ihre Migration bereitstellen? – Gazler

+0

@Gazler meine Frage bearbeitet, die Migration hinzugefügt. –

Antwort

9

Da es auf der Datenbank beruht, müssen Sie die Referenzen in der Migration hinzufügen und die eigentliche Datenbankoperation ausführen. Sie müssen Repo.insert/1 oder Repo.update/1 mit Ihrem Änderungssatz anrufen und es wird dann {:error, changeset} zurückgeben.

Denken Sie daran, es gibt keine Objekte in Elixir noch in Ecto. Daher kann changeset.valid? niemals eine Datenbankoperation ausführen, es sind nur Daten, die eine Reihe von Änderungen widerspiegeln, die ausgeführt werden müssen, und der Zustand dieser Daten wird bei der Ausführung von Operationen wie Einfügen oder Aktualisieren transformiert.

Eine letzte Anmerkung, errors_on/2 wird immer einen neuen Änderungssatz zurückgeben und nicht den, mit dem Sie bis jetzt gearbeitet haben. Ihre letzte Zeile sollte wahrscheinlich sein:

+0

Vielen Dank für Ihre vollständige Antwort. In diesem Fall war das Problem woanders: Ich habe test db nach einigen Änderungen an Migrationen nicht neu erstellt. Das Schema war also ohne Einschränkungen. Außerdem habe ich die Änderungen vorgenommen, die Sie vorgeschlagen haben. Danke nochmal! :-) –

1

"auf die Datenbank zu verlassen" bedeutet, dass Sie einen Fremdschlüssel in Ihrem Datenbankmodell haben müssen.

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

, die einen Fremdschlüssel Prüfung zwischen der Mutter erzwingt und die untergeordnete Tabelle:

In der Migration sollten Sie so etwas wie diese gehabt haben.

+0

tatsächlich habe ich das bei meiner Migration gemacht. Ich habe meine Frage aktualisiert und den Migrationscode hinzugefügt. –

Verwandte Themen