创建 ActiveRecord 模型时出现问题:保存时数据丢失

发布于 2024-08-03 23:34:18 字数 1090 浏览 1 评论 0原文

我在我正在开发的 Sinatra 应用程序中使用 ActiveRecord 在数据库中创建新模型行时遇到问题。创建有问题的对象时没有任何错误(使用保存!,不会引发异常),但我为保存指定的大部分数据不存在。

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 中构建了多个站点。这个问题完全让我困惑。谁能告诉我我做错了什么?

I'm having trouble creating a new model row in the database using ActiveRecord in a Sinatra app I'm developing. The object in question is being created without any errors (using save!, no exceptions are raised), but most of the data I specify for the save is not present.

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

All inspections on the data from the project object I'm sending as well as the new meta object I'm creating show that the data is present. But when I do a meta.inspect before and after the save, it shows that all the data (aside from project_id) is in it's default state (zeroes). I've also checked meta.errors.nil? and sure enough, there aren't any errors after the save.

What is most puzzling is that if I turn around and get a new meta instance with that project_id and put the data in, it saves no problem to the db.

This is frustrating me because I've built several sites in Rails and Sinatra with ActiveRecord. This one issue is completely perplexing me. Can anyone tell me what I'm doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

粉红×色少女 2024-08-10 23:34:18

以下是其工作原理

  1. 首次访问模型时,将检索相应数据库表中的列并将其存储在模型数据中。可以通过 ::columns 类方法检索此信息。

  2. 当您访问某些模型的属性时,Ruby 在类中找不到相应的方法并启动#method_missing 方法。该方法检查模型的 :: 以检查相应的列是否存在。 该模型的属性时,将直接调用访问器方法,而无需调用#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

模型有一个特殊的访问器 - #属性 -- 返回“column_name => value”哈希值。

注意:每列的数据存储在模型实例内的特殊哈希实例中,而不是存储在“@column_name”实例变量中。当您使用#attr_accessor 定义访问器时,您会阻止通过#method_missing 定义属性访问器的常用方法。您的数据存储在实例变量中,而不是“attributes”哈希中,因此它没有保存到数据库中。

如果要向模型添加新属性,实际上需要向与该模型对应的数据库表添加列,然后重新加载整个应用程序。

Here is how it works

  1. On first access to model, columns from corresponding database table are retrieved and stored inside model data. This information can be retrieved through ::columns class method.

  2. When you access some model's attribute, Ruby doesn't find corresponding method in class and launches #method_missing method. That method inspects model's ::columns to check if corresponding column exists. If so, it creates an accessors for that column so that next time you access that model's attribute, an accessor method will be called directly, without need to call #method_missing (the later is slower).

The accessors look like this:

def my_attribute
  read_attribute(:my_attribute)
end

def my_attribute=(value)
  write_attribute(:my_attribute, value)
end

For #read_attribute and #write_attribute methods there is a shortcut: #[] and #[]=. If for some reason you will need to access underlying data directly (e.g. do some data conversion), you can write them short:

def my_attribute
  self[:my_attribute]
end

def my_attribute=(value)
  self[:my_attribute] = value
end

Model has a special accessor -- #attributes -- which returns a "column_name => value" Hash.

NOTE: the data for each column is stored in a special Hash instance inside your model instance, not in "@column_name" instance variables. When you define accessors with #attr_accessor, you block the usual way of defining attribute accessors via #method_missing. Your data is stored in instance variables instead of "attributes" hash, so it is not saved into database.

If you want to add new attribute to your model, you actually need to add column to database table that correspond to that model and then reload the whole application.

﹉夏雨初晴づ 2024-08-10 23:34:18

数据库字段和临时 attr_accessor 声明的属性之间存在重要区别。如果您已经声明了列,则不需要 attr_accessor 声明。

请记住,数据应存储在模型的属性属性中才能正确保存,而不是作为单独的实例变量。

例如,要查看计划保存的内容:

class MyModel < ActiveRecord::Base
  attr_accessor :not_saved
end

model = MyModel.new(:not_saved => 'foo')

puts model.attributes.inspect

有一些方法可以获取有关模型中可用列的信息,例如:

MyModel.columns_names

There's an important distinction between database fields and temporary attr_accessor declared properties. If you've declared your columns, then attr_accessor declarations are unnecessary.

Keep in mind that the data should be stored in the model's attributes property to be properly saved, not as individual instance variables.

For example, to see what's scheduled to be saved:

class MyModel < ActiveRecord::Base
  attr_accessor :not_saved
end

model = MyModel.new(:not_saved => 'foo')

puts model.attributes.inspect

There are methods to get information on what columns are available in a model, such as:

MyModel.columns_names
热血少△年 2024-08-10 23:34:18

attr_accessors 永远不会保存到数据库中。这些是实例内的内部变量。如果要保存值,则必须创建真实的列。

进行迁移以声明列,然后重试。

The attr_accessors will never be saved to the DB. These are internal variables within the instance. If you want to save the values, you have to create real columns.

Make a migration to declare the columns then try again.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文