我如何在rails中构建一个复杂的表单,使我能够关联或创建一个新的相关实体? [英] How do I build a complex form in rails which allows me to associate or create a new related entity?

查看:91
本文介绍了我如何在rails中构建一个复杂的表单,使我能够关联或创建一个新的相关实体?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关于复杂的rails复杂表单的stackoverflow有很多问题,他们似乎都指向Ryan Bates关于这个主题的很好的railscast演示: part1 第2部分



这对于它的功能非常棒,但我没有看到有关您可能希望创建新的子对象的情况的问题,或者您可能想要将对象已经存在。

在我的情况下,我希望用户能够创建新的事件。作为其中的一部分,他们需要说出谁参与了事件。大约一半的时间,被添加到事件中的人员已经存在于数据库中。在这种情况下,应鼓励用户使用这些现有记录与创建新记录。

关于如何在表单中处理这种复杂性的任何建议?

作为解决方案的一部分,您是否推荐,如果用户没有找到应用程序继续前进的人并且在父代之前即时创建它对象被提交?我的想法(但有兴趣听取建议)是,用户应该使用现有的人员记录(如果有),但如果用户需要创建新人员记录,则该新记录不会提交给数据库,直到用户提交事件。想要做什么?

解决方案

您好:要完成您需要做的事情,您需要执行以下几个简单步骤:
1 )在模型类(您的案例中的事件和人员类)中,请确保您放置类似这样的内容:

  class Incident< ActiveRecord :: Base 
has_and_belongs_to_many:people
#请注意,如果您希望能够从Incident窗体中删除某个人(以解除关联),请将true置于:allow_destroy =>对于
accepters_nested_attributes_for:people,:allow_destroy => false,:reject_if => :all_blank
validates_associated:people #so您将无法保存无效的人物对象
attr_accessible:date_occur,:location,:people_attributes#请注意:此处的people_attributes
#您的事件验证像往常一样...
end

class Person< ActiveRecord :: Base
has_and_belongs_to_many:事件
#下面这行允许批量分配,基本上它允许您从事件表单创建人员
attr_accessible:first_name,:last_name,:dob
#像往常一样进行人员验证...
结束



<2>在视图方面,最简单的方法是修改事件表单文件(app / view / incidents / _form.html.erb),以允许用户将现有的和新的人员分配给事件:

 #app / view / incidents / _form.html.erb 

<%= semantic_form_for(@incident)do | f | %GT;
<%if @ incident.errors.any? %GT;
< div id =error_explanation>
< h2><%= pluralize(@ incident.errors.count,error)%>禁止保存此事件:< / h2>
< ul>
<%@ incident.errors.full_messages.each do | msg | %GT;
< li><%= msg%>< / li>
<%end%>
< / ul>
< / div>
<%end%>

< div class =field>
<%= f.label:date_occur%>< br />
<%= f.datetime_select:date_occur%>
< / div>
< div class =field>
<%= f.label:location%>< br />
<%= f.text_field:location%>
< / div>

<%= f.input:people,:as => :select,:collection => Hash [Person.all.map {| p | [p.first_name +' - '+ p.last_name,p.id]}]%>

<%= f.fields_for:people,Person.new()do | new_person_form | %GT;
< div class =incident-people new-person>
<%= new_person_form.inputs:name =>'Add New person'do%>
<%= new_person_form.input:first_name,:label =>'名字:'%>
<%= new_person_form.input:last_name,:label =>'姓氏:'%>
<%= new_person_form.input:dob,:label =>'出生日期:'%>
<%end%>
< / div>
<%end%>


< div class =actions>
<%= f.submit%>
< / div>
<%end%>

3)最后,您需要修改更新并为Incident控制器创建方法,如下所示:

 #app / controllers / incident_controller.rb 
def create
selected_people = params [:incident] [: person_ids] .keep_if {| v | v.present? }
params [:incident] .delete(:person_ids)
@incident = Incident.new(params [:incident])
@ incident.people = Person.find(selected_people)

respond_to do | format |
if @ incident.save
format.html {redirect_to @incident,notice:'Incident was created created。'}
format.json {render json:@incident,status :::created, location:@incident}
else
format.html {render action:new}
format.json {render json:@ incident.errors,status::unprocessable_entity}
结束
结束
结束

def更新
selected_people = params [:incident] [:person_ids] .keep_if {| v | v.present? }
params [:incident] .delete(:person_ids)
@incident = Incident.find(params [:id])
@ incident.people = Person.find(selected_people)
respond_to do | format |
if @ incident.update_attributes(params [:incident])
format.html {redirect_to @incident,notice:'Incident was successfully updated。'}
format.json {head:no_content}
else
format.html {render action:edit}
format.json {render json:@ incident.errors,status::unprocessable_entity}
end
结束
结束

就是这样,让我知道你是否需要进一步的帮助。
FedeX

There are a lot of question on stackoverflow about rails complex forms and they all seems to point to Ryan Bates' nice railscast demos on this subject: part1 and part2.

That's great for what it does, but I don't see questions addressing the situation where you may want to create new children objects, or you may want to associate objects that already exist.

In my case I want the user to be able to create a new Incident. As part of that they need to say who was involved in the incident. About half the time, the People being added to the Incident already exist in the db. In that case the user should be encouraged to use those existing records vs creating new ones.

Any suggestions on how to handle this complexity in the form?

And as part of the solution, do you recommend, that if the user doesn't find the person that the app goes ahead and creates it on the fly BEFORE the parent object gets submitted? My thinking (but interested in hearing recommendations) is that the user should use an existing Person record if there is one, but that if the user needs to create a NEW Person record, that this new record isn't submitted to the DB until the user submits the incident. Thoughts?

解决方案

Hi to accomplish what you need to do, there are a few easy steps to follow: 1) In the model classes (Incident and Person classes in your case) make sure you put something like this:

class Incident < ActiveRecord::Base
    has_and_belongs_to_many :people
    # Note if you want to be able to remove a person from the Incident form (to de-associate) put true in :allow_destroy => true on the accepts_nested_attributes_for
    accepts_nested_attributes_for :people, :allow_destroy => false, :reject_if => :all_blank
    validates_associated :people #so you won't be able to save invalid people objects
    attr_accessible :date_occur, :location, :people_attributes # note the :people_attributes here
    #do your Incident validations as usual...        
end

class Person < ActiveRecord::Base
    has_and_belongs_to_many :incidents
    #the following line it's to allow mass assignment, basically it will allow you to create people from the Incident form
    attr_accessible :first_name, :last_name, :dob 
    #do your Person validations as usual...
end

2) In the view side, the easiest way will be modifying the Incident form file (app/view/incidents/_form.html.erb) to allow the user to assign existing and create new people to the Incident:

# app/view/incidents/_form.html.erb

<%= semantic_form_for(@incident) do |f| %>
    <% if @incident.errors.any? %>
    <div id="error_explanation">
        <h2><%= pluralize(@incident.errors.count, "error") %> prohibited this incident from being saved:</h2>
        <ul>
            <% @incident.errors.full_messages.each do |msg| %>
                <li><%= msg %></li>
            <% end %>
        </ul>
    </div>
    <% end %>

    <div class="field">
        <%= f.label :date_occur %><br />
        <%= f.datetime_select :date_occur %>
    </div>
    <div class="field">
        <%= f.label :location %><br />
        <%= f.text_field :location %>
    </div>

    <%= f.input :people, :as => :select, :collection=>Hash[Person.all.map { |p| [p.first_name + ' - ' + p.last_name, p.id] }] %>

    <%= f.fields_for :people, Person.new() do |new_person_form| %>
        <div class="incident-people new-person">
            <%= new_person_form.inputs    :name=>'Add New person' do %>
                <%= new_person_form.input :first_name, :label=>'First Name: ' %>
                <%= new_person_form.input :last_name, :label=>'Last Name: ' %>
                <%= new_person_form.input :dob, :label=>'Date of birth: ' %>
            <% end %>
        </div>
    <% end %>


  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

3) Lastly, you need to modify your update and create methods for the Incident controller as follow:

# app/controllers/incident_controller.rb
def create
    selected_people = params[:incident][:person_ids].keep_if{ |v| v.present? }
    params[:incident].delete(:person_ids)
    @incident = Incident.new(params[:incident])
    @incident.people = Person.find( selected_people )

    respond_to do |format|
        if @incident.save
            format.html { redirect_to @incident, notice: 'Incident was successfully created.' }
            format.json { render json: @incident, status: :created, location: @incident }
        else
            format.html { render action: "new" }
            format.json { render json: @incident.errors, status: :unprocessable_entity }
        end
    end
end

def update
    selected_people = params[:incident][:person_ids].keep_if{ |v| v.present? }
    params[:incident].delete(:person_ids)
    @incident = Incident.find(params[:id])
    @incident.people = Person.find( selected_people )
    respond_to do |format|
        if @incident.update_attributes(params[:incident])
            format.html { redirect_to @incident, notice: 'Incident was successfully updated.' }
            format.json { head :no_content }
        else
            format.html { render action: "edit" }
            format.json { render json: @incident.errors, status: :unprocessable_entity }
        end
    end
end

And that's it, let me know if you need further help. FedeX

这篇关于我如何在rails中构建一个复杂的表单,使我能够关联或创建一个新的相关实体?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆