2016-03-18 14 views
9

Mam taki abonament.Jak zaktualizować związek w systemie Ecto?

defmodule Rebirth.Subscription do 
    use Rebirth.Web, :model 

    schema "subscriptions" do 
    ... 
    belongs_to :user, Rebirth.User 
    ... 
    end 

    ... 

    def update_user(model, params \\ :empty) do 
    model 
    |> cast(params, @required_fields, @optional_fields) 
    |> cast_assoc(:user, required: false)  
    end 
end 

Chcę powiązać użytkownika do subskrypcji

więc próbowałem

Rebirth.Subscription.update_user(subscription, %{user_id: 1}) 

lub

Rebirth.Subscription.update_user(subscription, %{user: user}) 

Kiedy uruchomić go pojawia się następujący błąd:

** (ArgumentError) unknown assoc `user` in `cast_assoc` 

Jak zaktualizować identyfikator użytkownika?

Dzięki!

+0

Obecnie doświadczam tego samego problemu.:/ –

+0

Nie znalazłem jeszcze sposobu, aby to naprawić, ale kopiąc wokół, izolowałem problem do 'Ecto.Changeset.cast_relation/4'. W szczególności 'Map.get (types, key)'. Stowarzyszenie nie jest jednym z modeli "typów". –

+0

Cóż, moje "to środek nocy i jestem tym zmęczony", należy usunąć 'cast_assoc' i dodać' user_id' do listy wymaganych parametrów. To przyzwoite tymczasowe obejście. –

Odpowiedz

11

cast_assoc służy do „szarego związane modelu” i może być używany tylko z has_one i has_many. Relacja belongs_to definiuje obcy identyfikator w modelu, w którym jest wywoływany. has_many i has_one polegać na "innym" obiekcie posiadającym klucz obcy.

Jeśli tworzysz obiekt, który ma wiele innych obiektów, warto sprawdzić je wszystkie, jeśli są prawidłowe. cast_assoc wywoła cast w odpowiednich modułach.

Twój użytkownik może mieć wiele subskrypcji (prawdopodobnie zgaduję tutaj), więc nie ma sensu tworzyć użytkownika podczas tworzenia subskrypcji i sprawdzać, czy użytkownik jest ważny przez cast_assoc. Zwykle w tym przypadku użytkownik będzie już istnieć w bazie danych.

W twoim przypadku chcesz tylko sprawdzić, czy wiąże modelu istnieje w gipsie, więc należy używać:

|> assoc_constraint(:user) 

ta nie sprawdza użytkownika, ale sprawdza, czy dana user_id istnieje w bazie danych. Teraz, gdy chcesz zaktualizować subskrypcję dla użytkownika można to zrobić:

user = Repo.get(User, id) 
subscription = Ecto.build_assoc(user, :subscriptions, other_fields_as_map) 

Uwaga wymaga has_one lub has_many na modelu użytkownika.

Albo można po prostu zaktualizować identyfikator użytkownika, jak próbował wcześniej:

Rebirth.Subscription.update_user(subscription, %{user_id: 1}) 

i tym razem sprawdzi bazę danych, jeśli id ​​dany użytkownik istnieje, ale nie będzie w stanie przejść całą obiekt użytkownika tutaj.

A jeśli chcesz zaktualizować powiązanego użytkownika, będziesz musiał zrobić to jawnie w dwóch krokach. a) uzyskać użytkownika, b) zaktualizować użytkownika za pomocą zestawu zmian zdefiniowanego w jego module.

Ostatnia uwaga, jeśli nie dokonujesz oddzielnego sprawdzania poprawności dla aktualizacji użytkowników (i nie sądzę, że powinieneś w tym scenariuszu), dobrze byłoby zmienić nazwę funkcji z update_user na changeset. Ten sam zestaw zmian może być używany do tworzenia i aktualizowania modeli.

+0

Dzięki! Czy to oznacza, że ​​jedynym sposobem na ustawienie użytkownika subskrypcji zamiast subskrypcji na użytkowniku jest ustawienie user_id? Jeśli się nie mylę, tworzy nową subskrypcję, po prawej: Ecto.build_assoc (użytkownik,: subskrypcje, other_fields_as_map)? –

+0

To prawda. Nie ma sensu ustawianie subskrypcji, ponieważ to użytkownik może mieć wiele subskrypcji. I tak, 'build_assoc' utworzy nową subskrypcję z ustawionym' user_id'. Jest to tylko skrót do uzyskania identyfikatora i ręcznego ustawienia go w subskrypcji, pamiętając także o skojarzeniach. – tkowal