当构建一个处理STI的表单时,如果我使用becomes
将对象转换为其父类,我将失去使用嵌套字段的能力。
我有两个型号
class Login < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :login
accepts_nested_attributes_for :login
end
我还有一些User的子类。
class Consumer < User
end
class Admin < User
end
class Agent < User
end
最初我遇到路由问题,因为Rails会假设我想要一个特定于当前类而不是父类的路由,所以我使用@user.becomes(User)
,这显然是处理它的方法。在大多数情况下,它工作正常,但这会导致@user.login
消失。
控制器
class Admin::UsersController < AdminController
load_and_authorize_resource
before_filter :authenticate_user!
def index
render 'index'
end
def new
@user = User.new
@user.build_login
render 'new'
end
def create
@user = User.new(user_params)
if @user.save
flash[:notice] = "Account confirmation instructions sent to #{@user.login.email}"
redirect_to new_user_path
else
flash.now[:error] = @user.errors.full_messages.to_sentence
# At this point, I can confirm that @user.login still exists...
render 'new'
end
end
private
def user_params
params.require(:user).permit(
:type,
:dealership_id,
login_attributes: [
:email
])
end
end
这是最相关的表单视图位
<%= simple_form_for(@user.becomes(User), html: {class: "user-form"}) do |f| %>
<%= f.simple_fields_for :login do |l| %>
<div class="field">
<%= l.label :email %><br />
<%= l.email_field :email %>
</div>
<% end %>
<div class="field">
<%= f.label :type %>
<%= f.select :type, options_for_select(current_user.types_can_create), include_blank: "- Select -", class: "form-control", id: "select_type" %>
</div>
<div class="actions">
<%= f.submit "Register" %>
</div>
<% end %>
:email
的文字字段未显示,因为@user.login
现在是nil
。使用becomes
时,这是预期的行为吗?
答案 0 :(得分:1)
之前只使用了becomes
,我只能证明我的经验 - 无论何时使用它,它实际上都会调用该类的新实例。
我不确定具体细节,但最重要的是,我猜测你的@user.becomes(User)
会覆盖@user.build_login
......
返回具有当前记录属性的指定klass的实例。
-
在您的情况下,我会明确设置路径(因为您正在使用User
):
<%= simple_form_for @user, url: user_path, method: :post html: {class: "user-form"} do |f| %>
答案 1 :(得分:0)
在Rails 5中,可以通过以下方式解决
<%= form_with scope: :user,
model: @user,
url: @user.id ? user_path(user) : users_path,
local: true do |f| %>
...
...
<% end %>
前面的代码指示FormBuilder使用@user属性填充字段,但是还指示它提交到主路由(而不是继承的路由),并使用scope:
对其进行指示。使用User
类名而不是子类名来命名字段。