ruby on rails Nuovi dati che non persistono nella colonna dell'array Rails su Postgres




ruby-on-rails arrays (3)

Ho un modello utente con una colonna di amici di tipo testo. Questa migrazione è stata eseguita per utilizzare la funzionalità di array con postgres:

add_column    :users, :friends, :text, array: true

Il modello utente ha questo metodo:

def add_friend(target)
  #target would be a value like "1234"
  self.friends = [] if self.friends == nil
  update_attributes friends: self.friends.push(target)
end

La seguente specifica passa fino a quando aggiungo user.reload dopo aver chiamato #add_friend :

it "adds a friend to the list of friends" do
  user = create(:user, friends: ["123","456"])
  stranger = create(:user, uid: "789")
  user.add_friend(stranger.uid)
  user.reload #turns the spec red
  user.friends.should include("789")
  user.friends.should include("123")
end

Questo succede anche nello sviluppo. L'istanza del modello viene aggiornata e ha il nuovo uid nell'array, ma una volta ricaricato o ricaricato l'utente in un'azione diversa, torna a quello che era prima che venisse chiamato il metodo add_friend .

Utilizzando Rails 4.0.0.rc2 e pg 0.15.1

Cosa potrebbe essere?


Sembra che il problema potrebbe essere il tuo uso di push , che modifica la matrice sul posto.

Non riesco a trovare un atm fonte più primaria, ma questo post dice:

Una cosa importante da notare quando si interagisce con l'array (o altri valori mutabili) su un modello. ActiveRecord non tiene traccia delle modifiche "distruttive" o sul posto. Questi includono l'inserimento e il pop - pop matrice, gli oggetti DateTime advance . Se si desidera utilizzare un aggiornamento "distruttivo", è necessario chiamare <attribute>_will_change! per far sapere a ActiveRecord che hai cambiato quel valore.


Sospetto che ActiveRecord non noti che l'array dei tuoi friends è cambiato perché, beh, il riferimento dell'array sottostante non cambia quando:

self.friends.push(target)

Ciò modificherà il contenuto dell'array ma la matrice stessa sarà ancora la stessa matrice. So che questo problema si presenta con la gemma postgres_ext in Rails3 e ho riscontrato questo problema :

L'attributo stringa non è contrassegnato come dirty, quando cambia con <<

Mi aspetto che Rails4 si comporti allo stesso modo.

La soluzione sarebbe quella di creare una nuova matrice piuttosto che provare a modificare la matrice sul posto:

update_attributes friends: self.friends + [ target ]

Esistono molti modi per creare un nuovo array aggiungendo un elemento a un array esistente, utilizzare quello che preferisci.


Se si desidera utilizzare il tipo di array Postgresql, è necessario rispettare il formato. Da Postgresql docs è il formato di input

'{10000, 10000, 10000, 10000}'

che non è ciò che gli friends.to_s ritorneranno. Nel rubino:

[1,2,3].to_s => "[1,2,3]"

Cioè, parentesi invece di parentesi graffe. Dovrai fare la conversione da solo.

Tuttavia, preferirei fare molto affidamento sulla serializzazione ActiveRecord (vedi serialize ). Il database non ha bisogno di sapere che il valore è in realtà un array, cioè il modello di dominio che perde nel database. Lascia che Rails faccia la sua cosa e incapsuli quell'informazione; sa già come serializzare / deserializzare il valore.

Nota: questa risposta è applicabile a Rails 3, non a 4. Verrò qui nel caso in cui aiuti qualcuno in futuro.





rails-activerecord