2016-04-28 65 views
0

我在Rails应用程序中使用Paperclip来验证文件附件并将它们加载到AWS S3中。期望的行为是用户在提交记录表单时能够上传多个“RecordAttachments”(文件附件)。Rails:在保存父项之前保存子元素

问题在于,只要用户选择要使用文件选择器上载的文件,无论用户是否提交了“记录”表单,我都希望立即开始上传RecordAttachments。我也想确保每个RecordAttachment都被映射到它所属的记录。这一定是可能的,但我还不确定如何构建逻辑。

目前,我可以允许用户在填写并提交记录表单时创建多个RecordAttachments,但我想立即开始上传文件,以便我可以向用户显示表单页面上的每个上载进度。下面是允许用户提交多个RecordAttachments一个记录代码:

#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 

并且子元素:

#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 

记录控制器:

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 

我的形式如下:

<%= 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 %> 

考虑到此设置,是否有人知道任何方法,只要用户选择它们就可以开始上传RecordAttachments,而不是在他们提交记录表单时?

在用下面的@ShamSUP对此进行了说明之后,以下是我的想法:在页面加载时,检查用户是否有Record_id为零的任何RecordAttachments。如果是这样,请删除它们。然后用户选择一个或多个文件。对于每一个,将recordAid设置为nil的RecordAttachment表中保存一行。然后,如果/当用户成功提交其记录表单时,找到所有具有record_id == nil的用户的RecordAttachments,并将每个record_id设置为当前Record的id。

这似乎是一个合理的解决方案吗?有更好/更简单的方法吗?我非常感谢他人在这个问题上提供的任何帮助!

回答

0

我最终使用了ng-file-upload,这是一个非常棒的文件上传库,用于Angular.js。 full source可用于其他人最终需要在保存父记录之前保存子节点的情况,但是最重要的是您希望将子节点从Javascript写入数据库,然后一旦保存父节点,就更新parent_id元素的每个孩子。

0

Javascript无法实际读取文件输入字段中的文件内容,因此不幸的是无法在用户尝试填写表单的其余部分时通过ajax提交。但这并不意味着你不能执行一些迂回的方法。

你可以尝试的东西是在表单页面加载时生成记录ID,然后在主表单中的隐藏输入中包含id,并将文件输入放在单独的表单中,或将它们移动到单独的表单中与页面加载后的JavaScript。在与文件输入相同的形式中,还包括生成的记录标识。

<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> 

之后,你有两种形式,您可以使用JavaScript文件形式提交给价值变动后的iframe,防止页面重新加载提交。

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

一些示例的javascript:More in-depth script here

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

因为你包括隐伏recordid场,你可以在用户完成其他部分之后使用此两种形式的关联。

+0

请注意,我没有太多关于ruby-specifics的信息,但这是客户端部分应该如何工作的。实际的上传脚本我不会帮助,但希望这可以指导您正确的方向。 – shamsup

+0

感谢ShamSUP!在页面加载中将记录ID分配给记录是否是线程安全的?此外,恶意用户是否能够使用类似Chrome开发工具的方式来编辑隐藏字段中的recordid,从而使RecordAttachments指向错误的Record ID?想到我可以保存RecordAttachments,将他们返回的ID传递给当前用户的会话散列,然后在记录表单完成时,更新用户刚刚上传的每个RecordAttachment的record_id字段,是否疯了? – duhaime

+0

会话变量肯定会走,我完全由于某种原因而使用它。 – shamsup

相关问题