简体   繁体   English

Rails:在保存父元素之前先保存子元素

[英]Rails: Save child elements before saving parent

I'm using Paperclip in a Rails app to validate file attachments and load them into AWS S3. 我在Rails应用程序中使用Paperclip来验证文件附件并将其加载到AWS S3中。 The desired behavior is for users to be able to upload multiple "RecordAttachments" (file attachments) when they submit a Record form. 所需的行为是使用户在提交“记录”表单时能够上载多个“ RecordAttachments”(文件附件)。

The catch is that I want to start uploading the RecordAttachments as soon as the user selects their files to upload with the file selector, regardless of whether the user has submitted the Record form. 问题在于,无论用户是否提交了“记录”表单,我都希望在用户使用文件选择器选择要上传的文件后立即开始上传RecordAttachments。 I also want to ensure each RecordAttachment is mapped to the Record to which it belongs. 我还想确保每个RecordAttachment都映射到它所属的Record。 This must be possible, but I'm not yet sure how to build out the logic. 这一定有可能,但是我还不确定如何建立逻辑。

Currently, I can allow users to create multiple RecordAttachments when they fill out and submit the Record form, but I'd like to start uploading the files immediately so that I can show users the progress of each upload on the form page. 目前,我可以允许用户在填写并提交“记录”表单时创建多个RecordAttachment,但是我想立即开始上传文件,以便可以在表单页面上向用户显示每次上传的进度。 Here's the code that lets users submit a Record with multiple RecordAttachments: 这是使用户可以提交带有多个RecordAttachments的Record的代码:

#app/models/record.rb
class Record < ActiveRecord::Base
  has_many :record_attachments
  accepts_nested_attributes_for :record_attachments

  # Ensure user has provided the required fields
  validates :title, presence: true
  validates :description, presence: true
  validates :metadata, presence: true
  validates :hashtag, presence: true
  validates :release_checked, presence: true

end

And the child element: 和子元素:

#app/models/record_attachment.rb  
class RecordAttachment < ActiveRecord::Base
  belongs_to :record
  validates :file_upload, presence: true

  # Before saving the record to the database, manually add a new
  # field to the record that exposes the url where the file is uploaded
  after_save :set_record_upload_url 

  # Use the has_attached_file method to add a file_upload property to the Record
  # class. 
  has_attached_file :file_upload

  # Validate that we accept the type of file the user is uploading
  # by explicitly listing the mimetypes we are willing to accept
  validates_attachment_content_type :file_upload,
    :content_type => [
      "video/mp4", 
      "video/quicktime"
  ]
end

Record Controller: 记录控制器:

class RecordsController < ApplicationController
  # GET /records/new
  def new
    @record = Record.new
  end

  def create
    # retrieve the current cas user name from the session hash
    @form_params = record_params()
    @form_params[:cas_user_name] = session[:cas_user]
    @record = Record.create( @form_params )

    respond_to do |format|
      if @record.save
        if params[:record_attachments]
          params[:record_attachments].each do |file_upload|
            @record.record_attachments.create(file_upload: file_upload)
          end
        end

        flash[:success] = "<strong>CONFIRMATION</strong>".html_safe + 
          ": Thank you for your contribution to the archive."
        format.html { redirect_to @record }
        format.json { render action: 'show', 
          status: :created, location: @record }
      else
        format.html { render action: 'new' }
        format.json { render json: @record.errors, 
          status: :unprocessable_entity }
      end
    end
  end
end

My form looks like this: 我的表格如下所示:

<%= form_for @record, html: { multipart: true } do |f| %>
  <div class="field">
    <%= f.label :title, "Title of Material<sup>*</sup>".html_safe %>
    <%= f.text_field :title %>
  </div>
  <!-- bunch of required fields followed by multiple file uploader: -->
  <div id="file-upload-container" class="field">
    <%= f.label :file_upload, "Upload File<sup>*</sup>".html_safe %>
    <div id="placeholder-box">File name...</div>
    <%= f.file_field :file_upload, 
      multiple: true,
      type: :file, 
      name: 'record_attachments[]', 
      id: "custom-file-upload",
      style: "display:none"
    %>
    <span class="btn btn-small btn-default btn-inverse" id="file-upload-button" onclick="$(this).parent().find('input[type=file]').click();">Browse</span>
  </div>

  <!-- bunch of fields followed by submit button -->
  <%= button_tag(type: 'submit', class: "btn btn-primary blue-button submit-button") do %>
    <i class="icon-ok icon-white"></i> SUBMIT
  <% end %>
<% end %>

Given this setup, are others aware of any approaches that would allow me to start uploading the RecordAttachments as soon as the user selects them, rather than when they submit the Record form? 有了这个设置,其他人是否知道有什么方法可以让我在用户选择它们之后(而不是在他们提交记录表单时)立即开始上载RecordAttachments?

After talking this out with @ShamSUP below, here's what I'm thinking: On page load, check if user has any RecordAttachments for which the record_id is nil. 在使用下面的@ShamSUP进行讨论之后,这就是我的想法:在页面加载时,检查用户是否有record_id为nil的RecordAttachments。 If so, delete them. 如果是这样,请删除它们。 Then user selects one or more files. 然后,用户选择一个或多个文件。 For each, save a row in the RecordAttachment table with the record_id set to nil. 对于每个记录,在recordAttachment表中保存一个record_id设置为nil的行。 Then, if/when the user successfully submits their Record form, find all of the user's RecordAttachments that have record_id == nil, and set the record_id of each to the current Record's id. 然后,如果/当用户成功提交其记录表单时,找到所有具有record_id == nil的用户RecordAttachment,并将每个record_id设置为当前Record的ID。

Does this seem like a reasonable solution? 这似乎是一个合理的解决方案吗? Is there a better/easier method? 有更好/更简便的方法吗? I would be very grateful for any help others can offer on this question! 我非常感谢其他人在这个问题上可以提供的任何帮助!

Javascript is unable to actually read the contents of files in the file input field, so they unfortunately can't be submitted via ajax while the user is trying to fill out the rest of the form. Javascript无法实际读取文件输入字段中的文件内容,因此,不幸的是,当用户尝试填写表单的其余部分时,无法通过ajax提交文件。 That doesn't mean that you can't perform some roundabout methods, though. 但是,这并不意味着您不能执行某些回旋方法。

Something you could try is generating the record id when the form page loads, then including the id in a hidden input in the main form, and either put the file input in a separate form, or move them into a separate form with javascript after page load. 您可以尝试在加载表单页面时生成记录ID,然后将ID包含在主表单的隐藏输入中,然后将文件输入置于单独的表单中,或者在页面后用javascript将它们移到单独的表单中加载。 In the same form as the file input, also include the record id that was generated. 以与文件输入相同的形式,还包括生成的记录ID。

<form id="main-form" enctype="multipart/form-data">
    <input type="hidden" name="recordid" value="123">
    <input type="text" name="title">

    .... etc fields
</form>
<form id="file-form" enctype="multipart/form-data" target="file_frame">
    <div id="placeholder-box">Select a file</div>
    <input type="file" multiple name="record_attachments">
    <input type="hidden" name="recordid" value="123">
</form> 

After you have the two forms, you can use javascript to submit the file-form to an iframe after the value changes, preventing page reload in submit. 拥有两种表单后,可以使用javascript在值更改后将文件表单提交到iframe,以防止在提交时重新加载页面。

<iframe name="file_frame" id="file_frame" src="path_to_upload_script" />

Some example javascript: More in-depth script here 一些示例javascript: 此处更深入的脚本

$("input.[name=record_attachments]").change(function () {
    $("#file_form').submit();
});

Because you're including the hiden recordid field, you can use this to associate the two forms after the user completes the other portion. 因为您包括了recordid字段,所以您可以在用户完成另一部分之后使用它来关联两个表单。

I ended up using ng-file-upload , an awesome file upload library for Angular.js, for this task. 我最终使用ng-file-upload ,这是Angular.js的出色文件上传库,用于此任务。 The full source is available in case others end up needing to save children before saving parent records, but the long and the short is that you'll want to write children to the db from Javascript, then once the parent is saved, update the parent_id element of each child. 如果其他人最终需要在保存父记录之前保存子项,则可以使用完整的源代码 ,但总的来说,就是要从Javascript将子项写入db,然后保存父项,然后更新parent_id每个孩子的元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM