Sto creando fluidi utilizzandorotto creare, salvare, aggiornare e distruggere il Join record quando utilizza Postgres UUID in Rails
create_table :users, { id: false } do |t|
t.uuid :uid, default: 'uuid_generate_v4()'
... other columns
e impostando self.primary_key = :uid
nei modelli.
In generale questo funziona correttamente con ActiveRecord e scrivo le associazioni has_many
e belongs_to
. Tuttavia, quando si attraversa una tabella di join (cioè has_many ... through:
, ho bisogno di scrivere SQL personalizzato per ottenere i record.
ho capito che posso in generale farlo scrivendo SQL personalizzata, vale a dire SELECT * FROM main_table JOIN join_table ON main_table.uid = cast(join_table.uid AS uuid) WHERE condition=true)
I' ve appena realizzato che ActiveRecord di create
, destroy
, save
e update
non lavoro sul modello di unirsi.
ho rattoppato i quattro metodi in modo da lavorare, ma è troppo complesso una sequenza per i miei gusti e probabilmente unoptimal. Qui sono i miei patch:
A volte, save
emette una ROLLBACK
la prima volta senza spiegazione. Quindi funziona la seconda volta. In altre situazioni (non sono sicuro del motivo, probabilmente durante l'aggiornamento), la prima volta che viene eseguita correttamente, ma se richiamata una seconda volta genera un errore TypeError. Vedere here per un'altra domanda su questo errore che non ha alcuna risposta su come salvare un join quando si utilizza uid anziché id. Ecco le mie altre patch (di lavoro).
def create(*args)
attrs = args[0]
raise(ArgumentError, "invalid args to bucket list create") unless attrs.is_a?(Hash)
bucket_list_photo = self.class.new(
attrs
)
bucket_list_photo.save
bucket_list_photo = BucketListPhoto.find_by(
bucket_list_uid: bucket_list_photo.bucket_list_uid,
photo_uid: bucket_list_photo.photo_uid
)
return bucket_list_photo
end
def update(*args)
# similar bug to save
attrs = args[0]
raise(ArgumentError, "invalid args to bucket list update") unless attrs.is_a?(Hash)
bucket_list_uid = self.bucket_list_uid
photo_uid = self.photo_uid
due_at = self.due_at
self.destroy
bucket_list_photo = self.class.new(
{
bucket_list_uid: bucket_list_uid,
photo_uid: photo_uid,
due_at: due_at
}.merge(attrs)
)
bucket_list_photo.save
bucket_list_photo = self.class.find_by(
photo_uid: photo_uid,
bucket_list_uid: bucket_list_uid
)
return bucket_list_photo # phew
end
def destroy
# patching to fix an error on #destroy, #destroy_all etc.
# the problem was apparently caused by custom primary keys (uids)
# see https://stackoverflow.com/a/26029997/2981429
# however a custom fix is implemented here
deleted_uids = ActiveRecord::Base.connection.execute(
"DELETE FROM bucket_list_photos WHERE uid='#{uid}' RETURNING uid"
).to_a.map { |record| record['uid'] }
raise "BucketListPhoto not deleted" unless (
(deleted_uids.length == 1) && (deleted_uids.first == uid)
)
ActiveRecord::Base.connection.query_cache.clear
# since, the cache isnt updated when using ActiveRecord::Base.connection.execute,
# reset the cache to ensure accurate values, i.e. counts and associations.
end
Ho persino assicurato che self.primary_key = :uid
in tutti i miei modelli.
Ho anche provato a sostituire uid
con id
ovunque e verificato che tutte le specifiche stavano passando (anche se ho lasciato nella patch). Tuttavia, ha ancora fallito quando ho rimosso la patch (rinominare le colonne uid
su id
non è stato risolto).
EDIT
In risposta ad alcuni commenti che ho provato il activeuuid
gemma (dove mi sono bloccato sulla an error) e ha deciso di passare totalmente oltre agli ID. Questo è fondamentalmente per semplicità visto che ho voglia di lanciare questa app al più presto.
Tuttavia, anche con questa correzione sono necessario patch save
, create
e update
. In realtà la patch delete
non funziona più e ho dovuto rimuoverla (basandosi sull'originale). Vorrei assolutamente evitare di dover fare queste patch e sto mantenendo la bounty aperta per questo motivo.
Le gemme come https://github.com/jashmenn/activeuuid o https://github.com/madpilot/has_uuid risolvono il problema? –
Approccio alternativo: perché non combinare entrambe le colonne 'id' e' uuid'? 'Id', che è un numero incrementale, è molto più efficiente da indicizzare e garantire l'integrità referenziale. E usare l'uuid per la presentazione rivolta verso l'esterno. E in questo modo tutto il codice delle rotaie continuerà a funzionare :) – nathanvda
@twairball ringrazia per queste raccomandazioni. Ho provato activeuuid ma non è stato in grado di correggere questo errore. has_uuid sembra interessante ma il suo readme dice che "non è abbastanza ben testato per essere rilasciato come una gemma", quindi ho intenzione di saltarlo per questa app di produzione. grazie per il tuo consiglio nathanvda. Ho finito per passare agli ID dagli UID completamente per motivi di semplicità. Comunque sto ancora usando le stesse patch (tranne che per distruggere). –