Спасибо за ваш вопрос. На самом деле это не так просто сделать. Вы хотите редактировать (один атрибут из) несколько записей в одной форме.
С небольшим толчком я придумал следующее:
В app/views/scooties_coupons/index.html.erb
:
<p id="notice"><%= notice %></p>
<h1>Scooties Coupons</h1>
<%= form_with(url: validate_coupons_path, method: 'patch') do |f| %>
<table>
<thead>
<tr>
<th>Valid</th>
<th>Coupon</th>
<th>Redeemed</th>
<th>First name</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @scooties_coupons.each do |scooties_coupon| %>
<tr>
<td>
<%= fields_for('scooties_coupons[]', scooties_coupon) do |cf|
cf.check_box(:validated)
end %>
</td>
<td><%= scooties_coupon.coupon %></td>
<td><%= scooties_coupon.redeemed %></td>
<td><%= scooties_coupon.first_name %></td>
<td><%= link_to 'Show', scooties_coupon %></td>
<td><%= link_to 'Edit', edit_scooties_coupon_path(scooties_coupon) %></td>
<td><%= link_to 'Destroy', scooties_coupon, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<%= f.submit %>
<% end %>
<br>
<%= link_to 'New Scooties Coupon', new_scooties_coupon_path %>
Содержит несколько трюков:
-
form_with(url: validate_coupons_path, method: 'patch')
устанавливает сообщение с патчем для сервера при отправке.
-
fields_for('scooties_coupons[]', scooties_coupon) do |cf|
cf.check_box(:validated)
end
устанавливает флажок для записи. Когда вы проверяете выходной HTML, вы обнаруживаете два поля следующим образом:
<input name="scooties_coupons[1][validated]" type="hidden" value="0">
<input type="checkbox" value="1" checked="checked"
name="scooties_coupons[1][validated]"
id="scooties_coupons_1_validated">
-
<%= f.submit %>
кнопка отправки
В app/controllers/scotties_coupons_controller.rb
:
def set_valid_coupons
to_valid = params[:scooties_coupons].select do |id, attrs|
attrs[:validated] == '1'
end
to_not_valid = params[:scooties_coupons].reject do |id, attrs|
attrs[:validated] == '1'
end
ScootiesCoupon.transaction do
ScootiesCoupon.where(id: to_valid.keys, validated: false).update_all(
validated:true)
ScootiesCoupon.where(id: to_not_valid.keys, validated: true).update_all(
validated:false)
end
redirect_to action: :index, notice: 'Validations updated'
end
- это очистка параметров для создания двух обновлений, которые устанавливают и очищают флаги validated
на записях так, чтобы они соответствовали тому, что было исправлено.
Во-первых, to_valid
содержит выбор параметров, у которых установлен флажок validated
.
Далее, to_not_valid
содержит выбор параметров, для которых не установлен флажок validated
. Когда атрибут validated
имеет значение, «1» проверяется. Этот код обрабатывает любое другое значение как , а не проверено.
Блок ScootiesCoupon.transaction do
делает два
обновления, которые он содержит практически на атомарном уровне, в том смысле, что в случае сбоя второго ActiveRecord выполнит откат первого.
Первое обновление выбирает записи, которые обе имеют:
- ключ, совпадающий с установленным флажком
validated
- текущее значение подтверждено как
false
Обновляет эти записи до значения, подтвержденного как true
Второе обновление выбирает записи, которые обе имеют:
- ключ, соответствующий ключам с
validated
, не отмечен
- текущее значение подтверждено как
true
Обновляет эти записи до значения, подтвержденного как false
Обновляя только те записи, которые нуждаются в изменении и не соответствуют значениям флажков, код избегает обновления каждой записи в таблице. В действительности, однако, если в таблице так много записей, представление индекса станет довольно громоздким. С сотнями или тысячами купонов будет довольно много рендеринга для браузера и прокрутки для человека, управляющего валидациями. Это другая проблема и другой вопрос.
In config/routes.rb
:
Rails.application.routes.draw do
resources :scooties_coupons
patch 'validate_coupons', to: 'scooties_coupons#set_valid_coupons'
end
добавляет действие контроллера для исправления записей.
Индекс отображается как, например:
При отправке консоль сервера выводит:
Started PATCH "/validate_coupons" for ::1 at 2019-06-25 17:12:58 -0300
Processing by ScootiesCouponsController#set_valid_coupons as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"6Rk...rA==",
"scooties_coupons"=>{
"1"=>{"validated"=>"1"}, "2"=>{"validated"=>"0"},
"3"=>{"validated"=>"0"}, "4"=>{"validated"=>"1"}
}, "commit"=>"Save "}
(0.1ms) begin transaction
↳ app/controllers/scooties_coupons_controller.rb:60
ScootiesCoupon Update All (0.4ms) UPDATE "scooties_coupons"
SET "validated" = 1
WHERE "scooties_coupons"."id" IN (?, ?)
AND "scooties_coupons"."validated" = ?
[["id", 1], ["id", 4], ["validated", 0]]
↳ app/controllers/scooties_coupons_controller.rb:61
ScootiesCoupon Update All (0.2ms) UPDATE "scooties_coupons"
SET "validated" = 0
WHERE "scooties_coupons"."id" IN (?, ?)
AND "scooties_coupons"."validated" = ?
[["id", 2], ["id", 3], ["validated", 1]]
↳ app/controllers/scooties_coupons_controller.rb:63
(0.1ms) commit transaction
↳ app/controllers/scooties_coupons_controller.rb:60
Redirected to http://localhost:3000/scooties_coupons?notice=Validations+updated
Completed 200 OK in 5ms (ActiveRecord: 0.7ms)
Существуют и другие приемы, позволяющие обновить запись, когда флажок установлен или снят, без отправки, через AJAX; тем не менее, начиная с отправленной формы, можно пройти большую часть пути без осложнений. Боюсь, AJAX - это довольно сложная задача.
Найти:
Они не сильно изменились за очень долгое время; так что скорее всего актуальны.
Кроме того, следующая реализация метода контроллера может показаться заманчивой:
def set_valid_coupons
update_params = {}
params.require(:scooties_coupons).each do |id, attrs|
update_params[id] = attrs.permit(:validated)
end
if (ScootiesCoupon.update(update_params.keys, update_params.values))
redirect_to action: :index, notice: 'Validations updated'
else
redirect_to action: :index, alert: 'Updates failed'
end
end
Однако обратите внимание, что вызывает несколько команд для базы данных на запись , как показано ниже:
Started PATCH "/validate_coupons" for 127.0.0.1 at 2019-06-25 15:33:29 -0300
Processing by ScootiesCouponsController#set_valid_coupons as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"2Ecnb8Sy/5L4m9wW+4QlSS5WXQTCUh92ALRcv9w69OGRiov/Z95ReX9tGBn7UNN/NXSl83PeKs8cIkLaGhAoDg==", "scooties_coupons"=>{"1"=>{"validated"=>"1"}, "2"=>{"validated"=>"0"}, "3"=>{"validated"=>"0"}, "4"=>{"validated"=>"0"}}, "commit"=>"Save "}
ScootiesCoupon Load (0.2ms) SELECT "scooties_coupons".* FROM "scooties_coupons" WHERE "scooties_coupons"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/controllers/scooties_coupons_controller.rb:48
ScootiesCoupon Load (0.1ms) SELECT "scooties_coupons".* FROM "scooties_coupons" WHERE "scooties_coupons"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
↳ app/controllers/scooties_coupons_controller.rb:48
ScootiesCoupon Load (0.1ms) SELECT "scooties_coupons".* FROM "scooties_coupons" WHERE "scooties_coupons"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
↳ app/controllers/scooties_coupons_controller.rb:48
ScootiesCoupon Load (0.1ms) SELECT "scooties_coupons".* FROM "scooties_coupons" WHERE "scooties_coupons"."id" = ? LIMIT ? [["id", 4], ["LIMIT", 1]]
↳ app/controllers/scooties_coupons_controller.rb:48
(0.0ms) begin transaction
↳ app/controllers/scooties_coupons_controller.rb:48
(0.0ms) commit transaction
↳ app/controllers/scooties_coupons_controller.rb:48
(0.0ms) begin transaction
↳ app/controllers/scooties_coupons_controller.rb:48
(0.0ms) commit transaction
↳ app/controllers/scooties_coupons_controller.rb:48
(0.0ms) begin transaction
↳ app/controllers/scooties_coupons_controller.rb:48
(0.0ms) commit transaction
↳ app/controllers/scooties_coupons_controller.rb:48
(0.0ms) begin transaction
↳ app/controllers/scooties_coupons_controller.rb:48
(0.0ms) commit transaction
↳ app/controllers/scooties_coupons_controller.rb:48
Redirected to http://localhost:3000/scooties_coupons?notice=Validations+updated
Completed 200 OK in 15ms (ActiveRecord: 1.0ms)