From dd4ad2f51b6e59c5081f1c3b32ad3227941ac047 Mon Sep 17 00:00:00 2001 From: Jibidus Date: Tue, 27 Oct 2015 19:22:50 +0100 Subject: [PATCH] Prevent setting null to not null foreign key --- .../rails_admin/ra.filtering-multiselect.js | 44 +++++++++++-------- .../_form_filtering_multiselect.html.haml | 1 + lib/rails_admin/config/fields/association.rb | 5 +++ .../edit/rails_admin_config_edit_spec.rb | 38 ++++++++++++++++ 4 files changed, 69 insertions(+), 19 deletions(-) diff --git a/app/assets/javascripts/rails_admin/ra.filtering-multiselect.js b/app/assets/javascripts/rails_admin/ra.filtering-multiselect.js index f90bab2110..17c18f0e69 100644 --- a/app/assets/javascripts/rails_admin/ra.filtering-multiselect.js +++ b/app/assets/javascripts/rails_admin/ra.filtering-multiselect.js @@ -17,6 +17,7 @@ return { query: query }; }, sortable: false, + removable: true, regional: { up: "Up", down: "Down", @@ -78,10 +79,13 @@ this.add = $('' + this.options.regional.add + ''); + this.columns.center.append(this.add); - this.remove = $('' + this.options.regional.remove + ''); + if (this.options.removable) { + this.remove = $('' + this.options.regional.remove + ''); + this.columns.center.append(this.remove); + } - this.columns.center.append(this.add).append(this.remove) if (this.options.sortable) { this.up = $('' + this.options.regional.up + ''); this.down = $('' + this.options.regional.down + ''); @@ -89,13 +93,13 @@ } this.selection = $(''); + this.columns.right.append(this.selection); - - this.removeAll = $('' + this.options.regional.clearAll + ''); - - this.columns.right.append(this.selection) - .append(this.removeAll); + if (this.options.removable) { + this.removeAll = $('' + this.options.regional.clearAll + ''); + this.columns.right.append(this.removeAll); + } this.selection.wrap('
'); @@ -127,19 +131,21 @@ widget.selection.trigger('change'); }); - /* Remove all from selection */ - this.removeAll.click(function(e){ - widget._deSelect($('option', widget.selection)); - e.preventDefault(); - widget.selection.trigger('change'); - }); + if (this.options.removable) { + /* Remove all from selection */ + this.removeAll.click(function(e){ + widget._deSelect($('option', widget.selection)); + e.preventDefault(); + widget.selection.trigger('change'); + }); - /* Remove from selection */ - this.remove.click(function(e){ - widget._deSelect($(':selected', widget.selection)); - e.preventDefault(); - widget.selection.trigger('change'); - }); + /* Remove from selection */ + this.remove.click(function(e){ + widget._deSelect($(':selected', widget.selection)); + e.preventDefault(); + widget.selection.trigger('change'); + }); + } var timeout = null; if(this.options.sortable) { diff --git a/app/views/rails_admin/main/_form_filtering_multiselect.html.haml b/app/views/rails_admin/main/_form_filtering_multiselect.html.haml index 8e59bd7b13..7f74ddfd93 100644 --- a/app/views/rails_admin/main/_form_filtering_multiselect.html.haml +++ b/app/views/rails_admin/main/_form_filtering_multiselect.html.haml @@ -26,6 +26,7 @@ :'edit-url' => (authorized?(:edit, config.abstract_model) ? edit_path(model_name: config.abstract_model.to_param, id: '__ID__') : ''), remote_source: index_path(config.abstract_model, source_object_id: form.object.id, source_abstract_model: source_abstract_model.to_param, associated_collection: field.name, current_action: current_action, compact: true), sortable: !!field.orderable, + removable: !!field.removable, cacheAll: !!field.associated_collection_cache_all, regional: { chooseAll: t("admin.misc.chose_all"), diff --git a/lib/rails_admin/config/fields/association.rb b/lib/rails_admin/config/fields/association.rb index 2d77545d05..18a7209434 100644 --- a/lib/rails_admin/config/fields/association.rb +++ b/lib/rails_admin/config/fields/association.rb @@ -57,6 +57,11 @@ def association # rubocop:disable TrivialAccessors @associated_collection_cache_all ||= (associated_model_config.abstract_model.count < 100) end + # Reader whether association's elements can be removed + def removable + association.foreign_key_nullable? + end + # Reader for the association's child model's configuration def associated_model_config @associated_model_config ||= RailsAdmin.config(association.klass) diff --git a/spec/integration/config/edit/rails_admin_config_edit_spec.rb b/spec/integration/config/edit/rails_admin_config_edit_spec.rb index dcf3716676..589ef4229f 100644 --- a/spec/integration/config/edit/rails_admin_config_edit_spec.rb +++ b/spec/integration/config/edit/rails_admin_config_edit_spec.rb @@ -818,6 +818,44 @@ class HelpTest < Tableless end end + describe 'has_many', active_record: true do + context 'with not nullable foreign key' do + before do + RailsAdmin.config FieldTest do + edit do + field :nested_field_tests do + nested_form false + end + end + end + @field_test = FactoryGirl.create :field_test + end + + it 'don\'t allow to remove element', js: true do + visit edit_path(model_name: 'FieldTest', id: @field_test.id) + is_expected.not_to have_selector('a.ra-multiselect-item-remove') + is_expected.not_to have_selector('a.ra-multiselect-item-remove-all') + end + end + + context 'with nullable foreign key' do + before do + RailsAdmin.config Team do + edit do + field :players + end + end + @team = FactoryGirl.create :team + end + + it 'allow to remove element', js: true do + visit edit_path(model_name: 'Team', id: @team.id) + is_expected.to have_selector('a.ra-multiselect-item-remove') + is_expected.to have_selector('a.ra-multiselect-item-remove-all') + end + end + end + describe 'fields which are nullable and have AR validations', active_record: true do it 'is required' do # draft.notes is nullable and has no validation