2013-04-27 16 views
28

Sono abbastanza nuovo per le rotaie e ho cercato di risolvere il problema tutta la notte senza fortuna.Aggiunta di una chiave esterna a un modello di rotaie

Ho creato 3 modelli: users, businesses e business_hours. Ho anche aggiunto le associazioni (business_hours belongs_to businesses which belongs_to users) e (user has_one business which has_many business_hours).

Leggendo i documenti in linea, sembra che ora sia necessario creare le chiavi esterne per queste relazioni nelle tabelle DB. Come faccio a farlo utilizzando le migrazioni di Rails ActiveRecord? Sto usando PostgreSQL come mio DB.

risposta

23

Prima di tutto quando si utilizza appartiene al metodo non utilizzare s alla fine della parola: business_hours belongs_to business which belongs_to user.

Ora crea una migrazione:

rails generate migration migration_name 

E nella migrazione aggiungere colonne:

class MigrationName < ActiveRecord::Migration 
    def change 
    add_foreign_key :business_hours, :businesses 
    add_foreign_key :businesses, :users 
    end 
end 

Run rake db:migrate. Questo è tutto.

+0

Devo anche aggiungere la colonna business_id per le imprese e la colonna user_id per gli utenti se già non li hanno? –

+0

Ok. Quando eseguo db: migrate ottengo questo errore: PG :: Errore: ERRORE: la relazione "business" non esiste –

+0

Spiacente, ho trovato il mio errore nel metodo di cambio def. Grazie per l'aiuto! –

5

Non l'ho provato con PostgreSQL ma almeno con MySQL Rails NON creare chiavi esterne, voglio dire non reali chiavi esterne di livello db. Tutto ciò che creano è un numero intero che viene nominato in base alla convenzione. Ciò significa che fuori dalla scatola non si ottiene l'indice su questa chiave estranea falsa (per una ricerca più rapida) e non c'è anche alcun controllo di integrità referenziale a livello di DB. Per ottenere che avete bisogno di fare qualcosa di simile:

ALTER TABLE your_table ADD CONSTRAINT fk_whatever_you_want_to_name_it FOREIGN KEY (foreign_key_name) REFERENCES another_table(its_primary_key) 

In una migrazione Rails è possibile passare questo come un argomento di tipo stringa alla funzione di "eseguire". L'aggiunta di una chiave esterna "reale" crea automaticamente un indice. Almeno per me questa è stata una brutta sorpresa.

+0

Questa è una novità per me. Vorrei che qualcun altro ci commentasse, perché sarebbe davvero una brutta sorpresa. Uso Postgres e guardando le tabelle in pgAdmin non sono elencate come chiavi esterne. Questo potrebbe essere ok per un progetto Rails only, ma cosa succede se ho bisogno di altri programmi per accedere al database? – tentimes

+0

È un po 'brutto, ma ho imparato che c'è anche una ragione dietro di esso. I binari stanno cercando di astrarre il db il più possibile, in modo che tu possa facilmente cambiare il livello di persistenza. Ma penso che sia qualcosa che non è sufficientemente sottolineato. Se vuoi puoi sempre usare add_index sui campi che vuoi come chiavi esterne. Oppure puoi scrivere le tue migrazioni in SQL o forse anche forzare le rotaie per eseguire le chiavi esterne in qualche modo. Ma la cosa è che non cercherete nemmeno un modo per farlo se non sapete che i binari non lo stanno facendo in primo luogo. Ha davvero senso? :-) – Renra

+0

In realtà ho scoperto che esiste una gemma chiamata Foreigner per correggere questo! Ha anche una gemma aggiuntiva chiamata Immigrant che funziona per tutto il db. L'aggiunta di un indice contrassegna qualcosa come una chiave esterna? Non lo sapevo - sono nuovo alle guide;) – tentimes

54

La risposta attualmente accettata su questo non è molto accurata in quanto non aggiunge una chiave esterna del database. È solo l'aggiunta di colonne integer.

In Rails 4.2.x, l'approccio attuale è:

http://guides.rubyonrails.org/active_record_migrations.html#foreign-keys

Creare una migrazione:

rails generate migration migration_name 

Per colonne esistenti, nella migrazione aggiungono le chiavi esterne così:

class MigrationName < ActiveRecord::Migration 
    def change 
    add_foreign_key :business_hours, :businesses 
    add_foreign_key :businesses, :users 
    end 
end 

Per Rails 4.x o se si sta aggiungendo una nuova colonnae voglio che sia una chiave esterna si può fare questo, dove probabilmente anche voi volete specificare l'indice come vero, ma non è parte del requisito per la chiave esterna:

http://edgeguides.rubyonrails.org/active_record_migrations.html#creating-a-migration

class MigrationName < ActiveRecord::Migration 
    def change 
    add_reference :business_hours, :business, index: true, foreign_key: true 
    add_reference :businesses, :user, index: true, foreign_key: true 
    end 
end 
+1

Grazie per aver condiviso questo Chris. Ho guardato dappertutto per capirlo. – mack

+0

Questo è quello che mi aspettavo quando ho cercato la risoluzione di questo problema. – Ratinahirana

+1

C'è un modo per specificare il nome per una colonna di chiave esterna del genere? Ho già avuto situazioni in cui c'erano più riferimenti all'interno dello stesso record a una tabella di dati specifica. Ciò significa che mi aspetterei di vedere (sourceClass, targetClass, fieldName), ma l'esempio sopra sembra solo contenere (sourceClass, targetClass). Qualcuno ha qualche informazione su questo? –

2

Rails 5 ora possibile aggiungere chiave esterna nelle migrazioni, vedi http://devdocs.io/rails~5.0/activerecord/connectionadapters/schemastatements#method-i-add_foreign_key.Così

add_foreign_key :articles, :authors 

crea

ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") 

Se si dispone di un modello di dati standard non si può fare.

add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id" 

che crea

ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id") 
Problemi correlati