我在使用我正在开发的Sinatra应用程序中使用ActiveRecord创建数据库中的新模型行时遇到问题。正在创建的对象没有任何错误(使用save!,没有引发异常),但我为保存指定的大多数数据都不存在。
class ProjectMeta < ActiveRecord::Base
attr_accessor :completion_ratio, :num_stories, :num_completed_stories, :original_target_date, :current_target_date
...
def self.create_from_project(project)
meta = ProjectMeta.new
meta.project_id = project.id
meta.num_stories = project.num_stories
meta.num_completed_stories = project.num_completed_stories
meta.completion_ratio = ProjectMeta.calculate_ratio(project.num_completed_stories, project.num_stories)
meta.current_target_date = project.current_target_date
meta.save!
meta
end
...
end
对我正在发送的项目对象的数据进行的所有检查以及我正在创建的新元对象都显示数据存在。但是当我在保存之前和之后执行meta.inspect
时,它会显示所有数据(除了project_id)都处于默认状态(零)。我还检查了meta.errors.nil?
,果然,保存后没有任何错误。
最令人费解的是,如果我转而使用该project_id获取一个新的元实例并将数据放入其中,它就不会给数据库带来任何问题。
这令我感到沮丧,因为我已经使用ActiveRecord在Rails和Sinatra中构建了几个站点。这个问题让我感到困惑。谁能告诉我我做错了什么?
答案 0 :(得分:6)
以下是它的工作原理
首次访问模型时,将检索相应数据库表中的列并将其存储在模型数据中。可以通过:: columns类方法检索此信息。
当您访问某个模型的属性时,Ruby在类中找不到相应的方法并启动#method_missing方法。该方法检查模型的:: columns以检查相应的列是否存在。如果是这样,它会为该列创建一个访问器,以便下次访问该模型的属性时,将直接调用访问器方法,而无需调用#method_missing(后者较慢)。
访问者看起来像这样:
def my_attribute
read_attribute(:my_attribute)
end
def my_attribute=(value)
write_attribute(:my_attribute, value)
end
对于#read_attribute和#write_attribute方法,有一个快捷方式:#[]和#[]=。如果由于某种原因你需要直接访问底层数据(例如进行一些数据转换),你可以写一下:
def my_attribute
self[:my_attribute]
end
def my_attribute=(value)
self[:my_attribute] = value
end
模型有一个特殊的访问者 - #attributes - 返回“column_name =&gt; value”哈希。
注意:每列的数据存储在模型实例内的特殊Hash实例中,而不是“@column_name”实例变量中。使用#attr_accessor定义访问器时,会阻止通过#method_missing定义属性访问器的常用方法。您的数据存储在实例变量中,而不是“attributes”哈希值,因此不会保存到数据库中。
如果要向模型添加新属性,实际上需要将列添加到与该模型对应的数据库表中,然后重新加载整个应用程序。
答案 1 :(得分:1)
数据库字段和临时attr_accessor声明的属性之间存在重要区别。如果您已声明了列,则不需要attr_accessor声明。
请记住,数据应存储在模型的attributes属性中,以便正确保存,而不是作为单独的实例变量。
例如,要查看计划保存的内容:
class MyModel < ActiveRecord::Base
attr_accessor :not_saved
end
model = MyModel.new(:not_saved => 'foo')
puts model.attributes.inspect
有一些方法可以获取有关模型中可用列的信息,例如:
MyModel.columns_names
答案 2 :(得分:0)
attr_accessors永远不会保存到数据库中。这些是实例中的内部变量。如果要保存值,则必须创建实际列。
进行迁移以声明列,然后重试。