Skip to content

Commit 3f67637

Browse files
committed
Support Mongoid's has_and_belongs_to_many custom primary_key
Closes #3097
1 parent e4d5b2f commit 3f67637

10 files changed

Lines changed: 84 additions & 13 deletions

File tree

lib/rails_admin/adapters/active_record/association.rb

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,13 @@ def klass
3030
end
3131

3232
def primary_key
33-
(options[:primary_key] || association.klass.primary_key).try(:to_sym) unless polymorphic?
33+
return nil if polymorphic?
34+
case type
35+
when :has_one
36+
association.klass.primary_key
37+
else
38+
association.association_primary_key
39+
end.try(:to_sym)
3440
end
3541

3642
def foreign_key
@@ -50,6 +56,17 @@ def foreign_inverse_of
5056
nil
5157
end
5258

59+
def key_accessor
60+
case type
61+
when :has_many, :has_and_belongs_to_many
62+
"#{name.to_s.singularize}_ids".to_sym
63+
when :has_one
64+
"#{name}_id".to_sym
65+
else
66+
foreign_key
67+
end
68+
end
69+
5370
def as
5471
options[:as].try :to_sym
5572
end

lib/rails_admin/adapters/mongoid/association.rb

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@ def klass
4141
end
4242

4343
def primary_key
44-
association.primary_key.to_sym rescue :_id
44+
case type
45+
when :belongs_to, :has_and_belongs_to_many
46+
association.primary_key.to_sym
47+
else
48+
:_id
49+
end
4550
end
4651

4752
def foreign_key
@@ -64,6 +69,19 @@ def foreign_inverse_of
6469
inverse_of_field.try(:to_sym)
6570
end
6671

72+
def key_accessor
73+
case macro.to_sym
74+
when :has_many
75+
"#{name.to_s.singularize}_ids".to_sym
76+
when :has_one
77+
"#{name}_id".to_sym
78+
when :embedded_in, :embeds_one, :embeds_many
79+
nil
80+
else
81+
foreign_key
82+
end
83+
end
84+
6785
def as
6886
association.as.try :to_sym
6987
end

lib/rails_admin/config/fields/association.rb

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ def association
1010
@properties
1111
end
1212

13+
def method_name
14+
association.key_accessor
15+
end
16+
1317
register_instance_option :pretty_value do
1418
v = bindings[:view]
1519
[value].flatten.select(&:present?).collect do |associated|
@@ -82,12 +86,7 @@ def associated_object_label_method
8286

8387
# Reader for associated primary key
8488
def associated_primary_key
85-
@associated_primary_key ||= associated_model_config.abstract_model.primary_key
86-
end
87-
88-
# Reader for the association's key
89-
def foreign_key
90-
association.foreign_key
89+
association.primary_key
9190
end
9291

9392
# Reader whether this is a polymorphic association

lib/rails_admin/config/fields/types/belongs_to_association.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ class BelongsToAssociation < RailsAdmin::Config::Fields::Association
2828
end
2929

3030
def associated_primary_key
31-
@associated_primary_key ||= association.primary_key
31+
association.primary_key
3232
end
3333

3434
def selected_id
35-
bindings[:object].send(foreign_key)
35+
bindings[:object].send(association.key_accessor)
3636
end
3737

3838
def method_name
39-
nested_form ? "#{name}_attributes".to_sym : association.foreign_key
39+
nested_form ? "#{name}_attributes".to_sym : super
4040
end
4141

4242
def multiple?

lib/rails_admin/config/fields/types/has_many_association.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class HasManyAssociation < RailsAdmin::Config::Fields::Association
1818
end
1919

2020
def method_name
21-
nested_form ? "#{super}_attributes".to_sym : "#{super.to_s.singularize}_ids".to_sym # name_ids
21+
nested_form ? "#{name}_attributes".to_sym : super
2222
end
2323

2424
# Reader for validation errors of the bound object

lib/rails_admin/config/fields/types/has_one_association.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def selected_id
2626
end
2727

2828
def method_name
29-
nested_form ? "#{name}_attributes".to_sym : "#{name}_id".to_sym
29+
nested_form ? "#{name}_attributes".to_sym : super
3030
end
3131

3232
def multiple?

spec/dummy_app/app/mongoid/managing_user.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
class ManagingUser < User
22
has_one :team, class_name: 'ManagedTeam', foreign_key: :manager, primary_key: :email, inverse_of: :user
33
has_many :teams, class_name: 'ManagedTeam', foreign_key: :manager, primary_key: :email, inverse_of: :user
4+
has_and_belongs_to_many :players, foreign_key: :player_names, primary_key: :name, inverse_of: :nil
5+
has_and_belongs_to_many :balls, primary_key: :color, inverse_of: :nil
46

57
def team_id=(id)
68
self.team = ManagedTeam.where(_id: id).first

spec/integration/fields/has_and_belongs_to_many_association_spec.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,27 @@
5252
is_expected.not_to have_css("a[href='/admin/comment/#{@comment3.id}']")
5353
end
5454
end
55+
56+
context "with Mongoid's custom primary_key option", mongoid: true do
57+
let(:user) { FactoryBot.create :managing_user, players: [players[0]], balls: [balls[0]] }
58+
let!(:players) { FactoryBot.create_list(:player, 2) }
59+
let!(:balls) { %w(red blue).map { |color| FactoryBot.create(:ball, color: color) } }
60+
before do
61+
RailsAdmin.config ManagingUser do
62+
field :players
63+
field :balls
64+
end
65+
end
66+
67+
it "allows update" do
68+
visit edit_path(model_name: 'managing_user', id: user.id)
69+
expect(find("select#managing_user_player_names option[value=\"#{players[0].name}\"]")).to be_selected
70+
expect(find("select#managing_user_ball_ids option[value=\"#{balls[0].color}\"]")).to be_selected
71+
select(players[1].name, from: 'Players')
72+
select(balls[1].rails_admin_default_object_label_method, from: 'Balls')
73+
click_button 'Save'
74+
expect(ManagingUser.first.players).to match_array players
75+
expect(ManagingUser.first.balls).to match_array balls
76+
end
77+
end
5578
end

spec/rails_admin/adapters/active_record/association_spec.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class ARComment < Tableless
6767
expect(subject.primary_key).to eq :id
6868
expect(subject.foreign_key).to eq :a_r_blog_id
6969
expect(subject.foreign_type).to be_nil
70+
expect(subject.key_accessor).to eq :a_r_blog_id
7071
expect(subject.as).to be_nil
7172
expect(subject.polymorphic?).to be_falsey
7273
expect(subject.inverse_of).to be_nil
@@ -85,6 +86,7 @@ class ARComment < Tableless
8586
expect(subject.primary_key).to eq :id
8687
expect(subject.foreign_key).to eq :ar_blog_id
8788
expect(subject.foreign_type).to be_nil
89+
expect(subject.key_accessor).to eq :a_r_post_ids
8890
expect(subject.as).to be_nil
8991
expect(subject.polymorphic?).to be_falsey
9092
expect(subject.inverse_of).to be_nil
@@ -162,6 +164,7 @@ class FieldTestWithSymbolForeignKey < FieldTest
162164
expect(subject.primary_key).to eq :id
163165
expect(subject.foreign_type).to be_nil
164166
expect(subject.foreign_key_nullable?).to be_truthy
167+
expect(subject.key_accessor).to eq :a_r_category_ids
165168
expect(subject.as).to be_nil
166169
expect(subject.polymorphic?).to be_falsey
167170
expect(subject.inverse_of).to be_nil
@@ -181,6 +184,7 @@ class FieldTestWithSymbolForeignKey < FieldTest
181184
expect(subject.primary_key).to be_nil
182185
expect(subject.foreign_key).to eq :commentable_id
183186
expect(subject.foreign_type).to eq :commentable_type
187+
expect(subject.key_accessor).to eq :commentable_id
184188
expect(subject.as).to be_nil
185189
expect(subject.polymorphic?).to be_truthy
186190
expect(subject.inverse_of).to be_nil
@@ -204,6 +208,7 @@ class FieldTestWithSymbolForeignKey < FieldTest
204208
expect(subject.primary_key).to eq :id
205209
expect(subject.foreign_key).to eq :commentable_id
206210
expect(subject.foreign_type).to be_nil
211+
expect(subject.key_accessor).to eq :a_r_comment_ids
207212
expect(subject.as).to eq :commentable
208213
expect(subject.polymorphic?).to be_falsey
209214
expect(subject.inverse_of).to be_nil

spec/rails_admin/adapters/mongoid/association_spec.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ class MongoNote
9090
expect(subject.foreign_key_nullable?).to be_truthy
9191
expect(subject.foreign_type).to be_nil
9292
expect(subject.foreign_inverse_of).to be_nil
93+
expect(subject.key_accessor).to eq :mongo_blog_id
9394
expect(subject.as).to be_nil
9495
expect(subject.polymorphic?).to be_falsey
9596
expect(subject.inverse_of).to be_nil
@@ -115,6 +116,7 @@ class MongoNote
115116
expect(subject.foreign_key_nullable?).to be_truthy
116117
expect(subject.foreign_type).to be_nil
117118
expect(subject.foreign_inverse_of).to be_nil
119+
expect(subject.key_accessor).to eq :mongo_post_ids
118120
expect(subject.as).to be_nil
119121
expect(subject.polymorphic?).to be_falsey
120122
expect(subject.inverse_of).to be_nil
@@ -135,6 +137,7 @@ class MongoNote
135137
expect(subject.foreign_key_nullable?).to be_truthy
136138
expect(subject.foreign_type).to be_nil
137139
expect(subject.foreign_inverse_of).to be_nil
140+
expect(subject.key_accessor).to eq :mongo_category_ids
138141
expect(subject.as).to be_nil
139142
expect(subject.polymorphic?).to be_falsey
140143
expect(subject.inverse_of).to be_nil
@@ -156,6 +159,7 @@ class MongoNote
156159
expect(subject.foreign_key_nullable?).to be_truthy
157160
expect(subject.foreign_type).to eq :commentable_type
158161
expect(subject.foreign_inverse_of).to be_nil
162+
expect(subject.key_accessor).to eq :commentable_id
159163
expect(subject.as).to be_nil
160164
expect(subject.polymorphic?).to be_truthy
161165
expect(subject.inverse_of).to be_nil
@@ -177,6 +181,7 @@ class MongoNote
177181
expect(subject.foreign_key_nullable?).to be_truthy
178182
expect(subject.foreign_type).to be_nil
179183
expect(subject.foreign_inverse_of).to be_nil
184+
expect(subject.key_accessor).to eq :mongo_comment_ids
180185
expect(subject.as).to eq :commentable
181186
expect(subject.polymorphic?).to be_falsey
182187
expect(subject.inverse_of).to be_nil
@@ -202,6 +207,7 @@ class MongoNote
202207
expect(subject.foreign_key_nullable?).to be_falsey
203208
expect(subject.foreign_type).to be_nil
204209
expect(subject.foreign_inverse_of).to be_nil
210+
expect(subject.key_accessor).to be_nil
205211
expect(subject.as).to be_nil
206212
expect(subject.polymorphic?).to be_falsey
207213
expect(subject.inverse_of).to be_nil
@@ -221,6 +227,7 @@ class MongoNote
221227
expect(subject.foreign_key).to be_nil
222228
expect(subject.foreign_key_nullable?).to be_falsey
223229
expect(subject.foreign_type).to be_nil
230+
expect(subject.key_accessor).to be_nil
224231
expect(subject.as).to be_nil
225232
expect(subject.polymorphic?).to be_falsey
226233
expect(subject.inverse_of).to be_nil

0 commit comments

Comments
 (0)