我遇到了simple_form问题。我有一个模型Order
,可以有许多texts
。 Text
模型可以具有 OR 文本内容或附加的文档。
我希望可以更新@order
并添加用户想要的任意数量的文本。在视图中,呈现了我的表单,然后在两个不同的部分中显示了一个simple_fields_for :texts
:
show.slim:
.card
= simple_nested_form_for(@order, url: steps_order_texts_url, html: { id: 'order-form' }) do |f|
.card-body
.row.mx-1
.col
= render('filedownloader', f: f)
= render('copytext', f: f)
.card-footer.fast-card-footer
= button_tag(type: 'submit', class: 'btn btn-primary btn-lg fast-btn', data: { disable_with: 'Please wait...' }) do
| Next
=< fa_icon('far', 'angle-right')
_filedownloader.slim:
.text-upload-content-wrapper
.filedownloader-wrapper-bottom
- secureId = SecureRandom.hex
.d-flex.flex-column.caption id="#{secureId}"
.d-flex.align-items-center
label.btn.btn-lg.btn-primary-dark.text-nowrap.filedownloader-custom-button for="text_document_#{secureId}"
| Choose a file
= f.simple_fields_for(:texts, @order.texts.build) do |ff|
= ff.input(\
:document,
label: false,
error: false,
input_html: { hidden: true,
id: "text_document_#{secureId}",
direct_upload: true)
.filedownloader-divider
.filedownloader-wrapper-footer
.d-inline-flex.align-items-center
span.filedownloader-addfile
= fa_icon('fal', 'plus-circle')
| Add a file
_copytext.slim:
.text-copy-content-wrapper
.filedownloader-copy-wrapper-bottom
= hidden_field_tag(:id, @order.id)
#recurring-texts
= f.simple_fields_for(:texts) do |ff|
- unless ff.object.persisted? && ff.object.content.blank?
.order-form-text
= ff.input( \
:content,
input_html: { \
class: 'form-control order-form-text-description',
rows: 4,},
required: true)
.d-flex.justify-content-end
= ff.link_to_remove( \
t('orders.remove_text'),
class: 'btn btn-danger btn-sm',
data: { confirm: "Are you sure?" })
= f.link_to_add( \
"#{fa_icon('far', 'plus-circle')} Add a text".html_safe,
:texts,
data: { target: '#recurring-texts' },
class: 'btn btn-primary-dark btn-lg')
问题在于,当我提交表单时,我的记录是重复的。我以为是因为有两个simple_fields_for :texts
,但是即使只有一个,文本也是重复的。另外,如果我尝试编辑文本,Rails会尝试创建具有相同ID的文本(因此会引发错误),而不是更新记录(当然id
中允许texts_attributes
)。
这是我创建一个文本时的日志,您可以看到ActiveRecord插入了两个文本:
Started PATCH "/steps/orders/311/texts" for ::1 at 2019-06-07 09:43:21 +0200
09:43:21 server.1 | Processing by Steps::TextsController#update as HTML
09:43:21 server.1 | Parameters: {"utf8"=>"✓", "authenticity_token"=>"eHZ1561Ly1SvWGdcj5jXasb+zLaA2PO+alY57K2ASaqyz5UwiACT9pclrSgdDfAnRjd+Pm0n9Ordk6JJb2WGHQ==", "file_upload_method_upload"=>"upload", "file_upload_method_copy"=>"copy", "order"=>{"texts_attributes"=>{"1"=>{"content"=>"I'm creating only one text", "_destroy"=>"false"}}}, "button"=>"", "order_id"=>"311"}
09:43:21 server.1 | User Load (1.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 2], ["LIMIT", 1]]
09:43:21 server.1 | ↳ /Users/robin/.rvm/gems/ruby-2.5.1/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
09:43:21 server.1 | Order Load (1.2ms) SELECT "orders".* FROM "orders" WHERE "orders"."id" = $1 LIMIT $2 [["id", 311], ["LIMIT", 1]]
09:43:21 server.1 | ↳ app/controllers/steps/base_controller.rb:17
09:43:21 server.1 | User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:57
09:43:21 server.1 | Profile Load (0.5ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."user_id" = $1 LIMIT $2 [["user_id", 2], ["LIMIT", 1]]
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:57
09:43:21 server.1 | (0.3ms) BEGIN
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:12
09:43:21 server.1 | Category Load (0.4ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT $2 [["id", 424], ["LIMIT", 1]]
09:43:21 server.1 | ↳ app/services/pricing.rb:10
09:43:21 server.1 | TextQuality Load (0.4ms) SELECT "text_qualities".* FROM "text_qualities" WHERE "text_qualities"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:12
09:43:21 server.1 | Text Load (0.6ms) SELECT "texts".* FROM "texts" WHERE "texts"."order_id" = $1 ORDER BY "texts"."created_at" ASC [["order_id", 311]]
09:43:21 server.1 | ↳ app/models/order.rb:124
09:43:21 server.1 | (0.3ms) SELECT SUM("texts"."word_number") FROM "texts" WHERE "texts"."order_id" = $1 [["order_id", 311]]
09:43:21 server.1 | ↳ app/models/order.rb:304
09:43:21 server.1 | Order Update (0.7ms) UPDATE "orders" SET "updated_at" = $1, "real_words_count" = $2 WHERE "orders"."id" = $3 [["updated_at", "2019-06-07 07:43:21.334874"], ["real_words_count", 0], ["id", 311]]
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:12
09:43:21 server.1 | Text Create (0.7ms) INSERT INTO "texts" ("order_id", "content", "created_at", "updated_at", "word_number") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["order_id", 311], ["content", "I'm creating only one text"], ["created_at", "2019-06-07 07:43:21.340588"], ["updated_at", "2019-06-07 07:43:21.340588"], ["word_number", 5]]
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:12
09:43:21 server.1 | Order Update All (0.5ms) UPDATE "orders" SET "texts_count" = COALESCE("texts_count", 0) + 1 WHERE "orders"."id" = $1 [["id", 311]]
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:12
09:43:21 server.1 | Text Update (0.4ms) UPDATE "texts" SET "word_number" = $1 WHERE "texts"."id" = $2 [["word_number", 5], ["id", 1374]]
09:43:21 server.1 | ↳ app/models/text.rb:323
09:43:21 server.1 | Text Create (0.3ms) INSERT INTO "texts" ("order_id", "content", "created_at", "updated_at", "word_number") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["order_id", 311], ["content", "I'm creating only one text"], ["created_at", "2019-06-07 07:43:21.346443"], ["updated_at", "2019-06-07 07:43:21.346443"], ["word_number", 5]]
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:12
09:43:21 server.1 | Order Update All (0.3ms) UPDATE "orders" SET "texts_count" = COALESCE("texts_count", 0) + 1 WHERE "orders"."id" = $1 [["id", 311]]
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:12
09:43:21 server.1 | Text Update (0.3ms) UPDATE "texts" SET "word_number" = $1 WHERE "texts"."id" = $2 [["word_number", 5], ["id", 1375]]
09:43:21 server.1 | ↳ app/models/text.rb:323
09:43:21 server.1 | (0.7ms) COMMIT
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:12
09:43:21 server.1 | (0.1ms) BEGIN
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:17
09:43:21 server.1 | (0.3ms) SELECT SUM("texts"."word_number") FROM "texts" WHERE "texts"."order_id" = $1 [["order_id", 311]]
09:43:21 server.1 | ↳ app/models/order.rb:304
09:43:21 server.1 | Order Update (0.5ms) UPDATE "orders" SET "updated_at" = $1, "real_words_count" = $2 WHERE "orders"."id" = $3 [["updated_at", "2019-06-07 07:43:21.355083"], ["real_words_count", 10], ["id", 311]]
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:17
09:43:21 server.1 | (0.7ms) COMMIT
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:17
09:43:21 server.1 | User Load (1.9ms) SELECT "users".id, "users".id, COUNT("texts".id)*1 AS count, MAX("users".assigned_texts_count) AS assigned_texts_count FROM "users" LEFT JOIN "candidacies" AS candidacies ON "users".id = candidacies.user_id LEFT JOIN "texts" AS texts ON "candidacies".id = texts.candidacy_id AND (texts.aasm_state = 'assigned') GROUP BY "users"."id" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1000]]
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:19
09:43:21 server.1 | (0.2ms) BEGIN
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:19
09:43:21 server.1 | (0.2ms) COMMIT
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:19
09:43:21 server.1 | User Load (0.9ms) SELECT "users".id, "users".id, COUNT("texts".id)*1 AS count, MAX("users".accepted_texts_count) AS accepted_texts_count FROM "users" LEFT JOIN "candidacies" AS candidacies ON "users".id = candidacies.user_id LEFT JOIN "texts" AS texts ON "candidacies".id = texts.candidacy_id AND (texts.aasm_state = 'accepted') GROUP BY "users"."id" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1000]]
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:19
09:43:21 server.1 | (0.1ms) BEGIN
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:19
09:43:21 server.1 | (0.2ms) COMMIT
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:19
09:43:21 server.1 | Order Load (0.8ms) SELECT "orders".id, "orders".id, COUNT("texts".id)*1 AS count, MAX("orders".texts_count) AS texts_count FROM "orders" LEFT JOIN "texts" AS texts ON "orders".id = texts.order_id GROUP BY "orders"."id" ORDER BY "orders"."id" ASC LIMIT $1 [["LIMIT", 1000]]
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:19
09:43:21 server.1 | (0.2ms) BEGIN
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:19
09:43:21 server.1 | Order Update All (0.4ms) UPDATE "orders" SET texts_count = 2 WHERE "orders"."id" = $1 [["id", 311]]
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:19
09:43:21 server.1 | (0.3ms) COMMIT
09:43:21 server.1 | ↳ app/controllers/steps/texts_controller.rb:19
09:43:21 server.1 | Redirected to http://localhost:3000/steps/orders/311/instructions
09:43:21 server.1 | Completed 302 Found in 73ms (ActiveRecord: 16.1ms)
我们可以看到以下内容:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"eHZ1561Ly1SvWGdcj5jXasb+zLaA2PO+alY57K2ASaqyz5UwiACT9pclrSgdDfAnRjd+Pm0n9Ordk6JJb2WGHQ==", "order"=>{"texts_attributes"=>{"1"=>{"content"=>"I'm creating only one text", "_destroy"=>"false"}}}, "button"=>"", "order_id"=>"311"}
只有一个文本,所以我真的不知道是什么问题... 有想法吗?
非常感谢
编辑:
steps / texts_controller.rb:
# frozen_string_literal: true
module Steps
class TextsController < BaseController
before_action(:redirect_unless_authorized)
def show
end
def update
if @order.update(order_params)
if @order.texts.none? { |text| text.content.present? } && !@order.documents?
flash.now.alert = t('.add_texts')
render(:show)
else
@order.update!(creation_step: 'instructions')
Text.counter_culture_fix_counts
jump_to(:instructions)
end
else
flash.now.alert = @order.errors.full_messages.join('. ') if @order.errors.messages.any?
render(:show)
end
end
def destroy
@text = @order.texts.find_by(id: params[:text_id])
@text.destroy
respond_to do |format|
format.json { head :no_content }
format.js { render layout: false }
end
end
private
def order_params
params.require(:order).permit(texts_attributes: %i[id document content _destroy])
end
def redirect_unless_authorized
return if admin_creates_for_user?
step = next_step
return unless step
jump_to(step, what_params)
end
def next_step
if !@order.paid?
:payment
elsif @order.user.profile&.invalid? && @order.paid?
:profile
end
end
end
end
编辑2: 我在更新前添加了一个byebug,然后检查(以防万一)order_params:
<ActionController::Parameters {"texts_attributes"=><ActionController::Parameters {"1"=><ActionController::Parameters {"content"=>"Hello world", "_destroy"=>"false"} permitted: true>} permitted: true>} permitted: true>
好的,只有一个文本。现在:
order = Order.new(order_params)
order.texts.size
#=> 2
order.pluck(:content)
#=> ['Hello world', 'Hello world']
... Wtf?
解决方案:
好,我不好在我的订单模型中,我的reject_if
上有一个accepts_nested_attributes_for
。在这个reject_if中,我正在构建带有属性的文本以测试某些东西。但是构建的文本之后并没有被销毁,因此将其添加到订单的文本中。