我对 Rail 模型的子类化有疑问。假设你有一个 User
模型和它的几个子类(用户类型),它们存储特定的方法和关联,例如:Director
、Admin
、 Trainee
,Instructor
等,这只是简单的“单表继承”。问题是双重的。
当您传递子类而不是基类时,路径/url 经常崩溃或做奇怪的事情。这是一个例子:
<% if user.enabled? %> <%= link_to 'Disable', disable_user_path(user) %> <% else %> <%= link_to 'Enable', enable_user_path(user) %> <% end %>
如果您传入一个
User
模型,它就可以正常工作。但是如果你传递一个子类,比如Admin
,它会抛出这个异常:No route matches {:controller=>"users", :action=>"disable", :id=>#<Admin id: 1, first_name: "Ken", ..., created_at: "2011-05-23 21:01:35", updated_at: "2011-05-23 21:04:28">}
显然,这是不正确的行为。我们怎样才能让 Rails 一直使用基类?
更令人不安的是表单(我正在使用
simple_form
)。假设您有一个/profile
表单。您希望所有User
子类都能平等地访问它,并且您不想在特殊情况下处理它们的子类;它应该是 100% 通用的。出于某种原因,如果用户是
Admin
,它会将参数哈希发布为params[:admin]
更糟糕的是,如果您查看表单的源代码,它实际上是
user[first_name]
而不是admin[first_name]
,所以肯定有问题!实例变量也是@user
,所以我不明白为什么它应该发布到params[:admin]
。
这是 (2) 的表单 View 代码:
<%= simple_form_for(@user, :url => profiles_path, :method => :put, :html => {:multipart => true}) do |f| %>
<%= f.error_notification %>
<div class="form">
<fieldset>
<legend>Personal Information</legend>
<%= f.input :first_name %>
<%= f.input :last_name %>
</fieldset>
<fieldset>
<legend>Credentials</legend>
<%= f.input :email %>
</fieldset>
<fieldset>
<legend>Preferences</legend>
<%= f.input :receive_email_notifications %>
<%= f.input :receive_newsletters %>
<%= f.input :allow_private_messages %>
</fieldset>
<fieldset>
<legend>Avatar</legend>
<p>
Select an image from your computer to use as your avatar. You will be given the oppurtunity
to crop this image further after your image has been uploaded.
</p>
<%= f.input :avatar, :as => :file, :label => "Select File" %>
<%= f.hidden_field :avatar_cache %>
</fieldset>
<div class="actions">
<%= f.button :submit, :value => "Update Profile" %>
<%= link_to 'Cancel', profiles_path %>
</div>
</div>
<% end %>
以下是首先呈现 View 和提交表单时的 Controller 操作:
def edit
@user = User.find(current_user.id)
end
def update
@user = User.find(current_user.id)
if @user.update_attributes(params[:user]) # <-- this bombs for Admin subclass
if params[:user][:avatar] # <-- this would bomb also.
redirect_to(crop_avatar_profiles_path)
else
redirect_to(profiles_path, :notice => 'Your profile was successfully updated.')
end
else
render :action => "edit"
end
end
def crop_avatar
@user = User.find(current_user.id)
end
def update_avatar
@user = User.find(current_user.id)
@user.crop(params[:x].to_i, params[:y].to_i, params[:h].to_i, params[:w].to_i)
redirect_to(profiles_path, :notice => 'Your profile and avatar was successfully updated.')
end
除了想出一些非常不雅的解决方案(尤其是表单问题)之外,我对如何解决它们一筹莫展。必须有更好的方法来处理这些情况。
最佳答案
= simple_form_for @fruit, :as => :fruit do |form|
@fruit
可以是任何类(Orange、Apple、Pear),它仍然会在输入前加上“fruit”前缀,因此每次都会给出一个 params[:fruit]
.
关于ruby-on-rails - Rails 3 模型子类让我发疯!请帮助更正 urls/params 哈希,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6103855/