2016-03-18 5 views
9

Ho un abbonamento come questo.Come aggiorno una relazione in 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 

voglio associare un utente alla sottoscrizione

Così ho provato

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

o

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

quando l'eseguo ottengo il seguente errore:

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

Come aggiorno user_id?

Grazie!

+0

Attualmente sto riscontrando lo stesso problema.:/ –

+0

Non ho ancora trovato un modo per risolverlo, ma scavando intorno, ho isolato il problema a 'Ecto.Changeset.cast_relation/4'. In particolare, 'Map.get (types, key)'. L'associazione non è uno dei "tipi" di modelli. –

+0

Bene, la mia soluzione "è il cuore della notte e sono stanco di questa" è rimuovere il 'cast_assoc' e aggiungere' user_id' alla lista dei parametri richiesti. È una soluzione temporanea decente. –

risposta

11

cast_assoc è usato per "modello fuso associato" e può essere utilizzato solo con has_one e has_many. La relazione belongs_to definisce l'ID esterno nel modello in cui è stato richiamato. has_many e has_one si basano su "l'altro" oggetto con chiave esterna.

Se si crea un oggetto con molti altri oggetti, è consigliabile controllarli tutti se sono validi. cast_assoc invocherà cast nei rispettivi moduli.

L'utente può avere molti abbonamenti (probabilmente, sto indovinando qui), quindi non ha senso creare utente quando si crea un abbonamento e verificare se l'utente è valido tramite cast_assoc. Solitamente in questo caso l'utente esisterà già nel database.

Nel tuo caso si desidera solo verificare se il modello associato esiste nel cast, così si dovrebbe usare:

|> assoc_constraint(:user) 

Questo non convalida utente, ma controlla se dato user_id esiste nel database. Ora, quando si desidera aggiornare l'abbonamento per un utente si può fare questo:

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

Nota richiede has_one o has_many sul modello utente.

Oppure si può semplicemente aggiornare l'ID utente come si è tentato prima:

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

e questa volta sarà controllare il database se non esiste dato user id, ma non sarà in grado di passare intero oggetto utente Qui.

E se si desidera aggiornare l'utente associato, sarà necessario farlo esplicitamente in due passaggi. a) ottenere l'utente, b) aggiornare l'utente usando il changeset definito nel suo modulo.

Nota finale, se non si effettua la convalida separata per l'aggiornamento degli utenti (e non penso che si dovrebbe in questo scenario), sarebbe bene rinominare la funzione da update_user a changeset. Lo stesso changeset può essere utilizzato per creare e aggiornare i modelli.

+0

Grazie! Questo significa che l'unico modo per impostare l'utente su una sottoscrizione piuttosto che le sottoscrizioni su un utente è impostare user_id? Se non sbaglio, questo fa un nuovo abbonamento, giusto: Ecto.build_assoc (utente,: subscriptions, other_fields_as_map)? –

+0

Questo è corretto. Non avrebbe senso impostare l'utente in abbonamento perché è l'utente che può avere molte iscrizioni. E sì, 'build_assoc' creerà una nuova sottoscrizione con il set' user_id'. Questa è solo una scorciatoia per ottenere manualmente l'id e impostarlo su abbonamento anche a conoscenza delle associazioni. – tkowal