【问题标题】:ActiveModel - View - Controller in Rails instead of ActiveRecord?ActiveModel - View - Rails 中的控制器而不是 ActiveRecord?
【发布时间】:2011-07-17 04:38:24
【问题描述】:

我正在尝试对我的模型使用 ActiveModel 而不是 ActiveRecord,因为我不希望我的模型与数据库有任何关系。

下面是我的模型:

class User
  include ActiveModel::Validations
  validates :name, :presence => true
  validates :email, :presence => true
  validates :password, :presence => true, :confirmation => true

  attr_accessor :name, :email, :password, :salt
  def initialize(attributes = {})
    @name  = attributes[:name]
    @email = attributes[:email]
    @password = attributes[:password]
    @password_confirmation = attributes[:password_confirmation]
  end
end

这是我的控制器:

class UsersController < ApplicationController
  def new
    @user = User.new
    @title = "Sign up"
  end
end

而我的看法是:

<h1>Sign up</h1>

<%= form_for(@user) do |f| %>
<div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
</div>
<div class="field">
    <%= f.label :email %><br />
    <%= f.text_field :email %>
</div>
<div class="field">
    <%= f.label :password %><br />
    <%= f.password_field :password %>
</div>
<div class="field">
    <%= f.label :password_confirmation, "Confirmation" %><br />
    <%= f.password_field :password_confirmation %>
</div>
<div class="actions">
    <%= f.submit "Sign up" %>
</div>
<% end %>

但是当我在浏览器中加载这个视图时,我得到了一个异常:

undefined method 'to_key' for User:0x104ca1b60

谁能帮我解决这个问题?

提前非常感谢!

【问题讨论】:

标签: ruby-on-rails ruby ruby-on-rails-3 activemodel


【解决方案1】:

我在 Rails 3.1 源代码中寻找解决办法,我认为这比在其他任何地方搜索更容易。早期版本的 Rails 应该是类似的。如果 tl;dr. 则跳转到最后。


当您致电form_for(@user) 时,您将结束此过程:

def form_for(record, options = {}, &proc)
  #...
  case record
  when String, Symbol
    object_name = record
    object      = nil
  else
    object      = record.is_a?(Array) ? record.last : record
    object_name = options[:as] || ActiveModel::Naming.param_key(object)
    apply_form_for_options!(record, options)
  end

并且由于@user 既不是字符串也不是对象,所以您将通过else 分支进入apply_form_for_options!。在apply_form_for_options! 内部,我们看到了这个:

as = options[:as]
#...
options[:html].reverse_merge!(
  :class  => as ? "#{as}_#{action}" : dom_class(object, action),
  :id     => as ? "#{as}_#{action}" : dom_id(object, action),
  :method => method
)

注意那段代码,它包含问题的根源和解决方案。 dom_id 方法调用 record_key_for_dom_id,如下所示:

def record_key_for_dom_id(record)
  record = record.to_model if record.respond_to?(:to_model)
  key = record.to_key
  key ? sanitize_dom_id(key.join('_')) : key
end

您可以致电to_keyto_key 方法由 ActiveRecord::AttributeMethods::PrimaryKey 定义,由于您没有使用 ActiveRecord,因此您没有 to_key 方法。如果您的模型中有一些行为类似于主键,那么您可以定义自己的 to_key 并保留它。

但是,如果我们回到apply_form_for_options!,我们会看到另一种解决方案:

as = options[:as]

因此您可以将:as 选项提供给form_for 以手动为您的表单生成一个DOM ID:

<%= form_for(@user, :as => 'user_form') do |f| %>

您必须确保 :as 值在页面中是唯一的。


执行摘要

  • 如果您的模型具有类似于主键的属性,则定义您自己的 to_key 方法来返回它。
  • 或者,为form_for 提供适当的:as 选项。

【讨论】:

  • 我真的很感谢你的努力,伙计!太精彩了。还有一个与此相关的小问题。对于 ActiveRecord,我刚刚将资源 :users 添加到我的 routes.rb 文件中,其中 users 是我的数据库表,它根据数据库中的 id 计算出用户路径。现在我正在使用它的 ActiveModel 我如何定义用户路径?
  • @Bilal:我不确定路线或 Rails 或 ActiveRecord 的哪个部分处理。我倾向于手动添加所有路由,并通过 AJAX 进行大部分交互。
  • 谢谢你 - 你的 :as 解决方案让我克服了第一个错误,但现在我看到了undefined method persisted?'`
  • @KarlRosaen:我需要更多背景信息才能知道发生了什么。
  • 感谢您的检查 - 对我来说,在 rails 3.1 中调试 form_helper.rb 代码时,我发现它调用了 is_presisted?在不带参数渲染 f.submit 时在表单对象上,所以我必须定义它并返回 false 以使 form_for 工作。或者,我可以传递一个参数 f.submit “提交表单”,它可以工作。
【解决方案2】:

看起来您应该研究一下(没有很好记录的)ActiveModel::Conversions 类

https://github.com/rails/rails/blob/3-1-stable/activemodel/lib/active_model/conversion.rb

  include ActiveModel::Conversion

  def persisted?
    false
  end

会成功的,同样适用于 Rails 4.2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-11
    • 1970-01-01
    • 1970-01-01
    • 2015-12-21
    相关资源
    最近更新 更多