Skip to content

Commit 99a6f9e

Browse files
committed
Add a foreign_key option to references while creating the table
Rather than having to do: create_table :posts do |t| t.references :user end add_foreign_key :posts, :users You can instead do: create_table :posts do |t| t.references :user, foreign_key: true end Similar to the `index` option, you can also pass a hash. This will be passed as the options to `add_foreign_key`. e.g.: create_table :posts do |t| t.references :user, foreign_key: { primary_key: :other_id } end is equivalent to create_table :posts do |t| t.references :user end add_foreign_key :posts, :users, primary_key: :other_id
1 parent a9c0c46 commit 99a6f9e

File tree

3 files changed

+94
-5
lines changed

3 files changed

+94
-5
lines changed

activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb

+23-4
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,12 @@ class TableDefinition
9494
# An array of ColumnDefinition objects, representing the column changes
9595
# that have been defined.
9696
attr_accessor :indexes
97-
attr_reader :name, :temporary, :options, :as
97+
attr_reader :name, :temporary, :options, :as, :foreign_keys
9898

9999
def initialize(types, name, temporary, options, as = nil)
100100
@columns_hash = {}
101101
@indexes = {}
102+
@foreign_keys = {}
102103
@native = types
103104
@temporary = temporary
104105
@options = options
@@ -286,6 +287,10 @@ def index(column_name, options = {})
286287
indexes[column_name] = options
287288
end
288289

290+
def foreign_key(table_name, options = {}) # :nodoc:
291+
foreign_keys[table_name] = options
292+
end
293+
289294
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
290295
# <tt>:updated_at</tt> to the table. See SchemaStatements#add_timestamps
291296
#
@@ -297,9 +302,12 @@ def timestamps(*args)
297302
column(:updated_at, :datetime, options)
298303
end
299304

300-
# Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
301-
# <tt>references</tt> and <tt>belongs_to</tt> are acceptable. The reference column will be an +integer+
302-
# by default, the <tt>:type</tt> option can be used to specify a different type.
305+
# Adds a reference. Optionally adds a +type+ column, if
306+
# <tt>:polymorphic</tt> option is provided. <tt>references</tt> and
307+
# <tt>belongs_to</tt> are acceptable. The reference column will be an
308+
# +integer+ by default, the <tt>:type</tt> option can be used to specify
309+
# a different type. A foreign key will be created if a +foreign_key+
310+
# option is passed.
303311
#
304312
# t.references(:user)
305313
# t.references(:user, type: "string")
@@ -310,11 +318,18 @@ def references(
310318
*args,
311319
polymorphic: false,
312320
index: false,
321+
foreign_key: false,
313322
type: :integer,
314323
**options
315324
)
316325
polymorphic_options = polymorphic.is_a?(Hash) ? polymorphic : options
317326
index_options = index.is_a?(Hash) ? index : {}
327+
foreign_key_options = foreign_key.is_a?(Hash) ? foreign_key : {}
328+
329+
if polymorphic && foreign_key
330+
raise ArgumentError, "Cannot add a foreign key on a polymorphic relation"
331+
end
332+
318333
args.each do |col|
319334
column("#{col}_id", type, options)
320335

@@ -325,6 +340,10 @@ def references(
325340
if index
326341
self.index(polymorphic ? %w(type id).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options)
327342
end
343+
344+
if foreign_key
345+
self.foreign_key(col.to_s.pluralize, foreign_key_options)
346+
end
328347
end
329348
end
330349
alias :belongs_to :references

activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb

+11-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,17 @@ def create_table(table_name, options = {})
204204
end
205205

206206
result = execute schema_creation.accept td
207-
td.indexes.each_pair { |c, o| add_index(table_name, c, o) } unless supports_indexes_in_create?
207+
208+
unless supports_indexes_in_create?
209+
td.indexes.each_pair do |column_name, index_options|
210+
add_index(table_name, column_name, index_options)
211+
end
212+
end
213+
214+
td.foreign_keys.each_pair do |other_table_name, foreign_key_options|
215+
add_foreign_key(table_name, other_table_name, foreign_key_options)
216+
end
217+
208218
result
209219
end
210220

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
require 'cases/helper'
2+
3+
if ActiveRecord::Base.connection.supports_foreign_keys?
4+
module ActiveRecord
5+
class Migration
6+
class ReferencesForeignKeyTest < ActiveRecord::TestCase
7+
setup do
8+
@connection = ActiveRecord::Base.connection
9+
@connection.transaction do
10+
@connection.create_table(:testing_parents, force: true)
11+
end
12+
end
13+
14+
teardown do
15+
@connection.execute("drop table if exists testings")
16+
@connection.execute("drop table if exists testing_parents")
17+
end
18+
19+
test "foreign keys can be created with the table" do
20+
@connection.create_table :testings do |t|
21+
t.references :testing_parent, foreign_key: true
22+
end
23+
24+
fk = @connection.foreign_keys("testings").first
25+
assert_equal "testings", fk.from_table
26+
assert_equal "testing_parents", fk.to_table
27+
end
28+
29+
test "no foreign key is created by default" do
30+
@connection.create_table :testings do |t|
31+
t.references :testing_parent
32+
end
33+
34+
assert_equal [], @connection.foreign_keys("testings")
35+
end
36+
37+
test "options hash can be passed" do
38+
@connection.change_table :testing_parents do |t|
39+
t.integer :other_id
40+
t.index :other_id, unique: true
41+
end
42+
@connection.create_table :testings do |t|
43+
t.references :testing_parent, foreign_key: { primary_key: :other_id }
44+
end
45+
46+
fk = @connection.foreign_keys("testings").find { |k| k.to_table == "testing_parents" }
47+
assert_equal "other_id", fk.primary_key
48+
end
49+
50+
test "foreign keys cannot be added to polymorphic relations when creating the table" do
51+
@connection.create_table :testings do |t|
52+
assert_raises(ArgumentError) do
53+
t.references :testing_parent, polymorphic: true, foreign_key: true
54+
end
55+
end
56+
end
57+
end
58+
end
59+
end
60+
end

0 commit comments

Comments
 (0)