我使用Rails 3.0.6和mongoID 2.0.2。最近我遇到了保存问题!重写setter时的方法(我正在尝试创建自己的嵌套属性)。
所以这是模型:
class FeedItem
include Mongoid::Document
has_many :audio_refs
def audio_refs=(attributes_array, binding)
attributes_array.each do |attributes|
if attributes[:audio_track][:id]
self.audio_refs.build(:audio_track => AudioTrack.find(attributes[:audio_track][:id]))
elsif attributes[:audio_track][:file]
self.audio_refs.build(:audio_track => AudioTrack.new(:user_id => attributes[:audio_track][:user_id], :file => attributes[:audio_track][:file]))
end
end
if !binding
self.save!
end
end
AudioRef模型(它只是audio_tracks和feed_items之间的缓冲区)是:
class AudioRef
include Mongoid::Document
belongs_to :feed_item
belongs_to :audio_track
end
和AudioTrack:
class AudioTrack
include Mongoid::Document
has_many :audio_refs
mount_uploader :file, AudioUploader
end
所以这里是FeedItem模型的规范,它不起作用:
it "Should create audio_track and add audio_ref" do
@audio_track = Fabricate(:audio_track, :user_id => @author.id, :file => File.open("#{Rails.root}/spec/stuff/test.mp3"))
@feed_item= FeedItem.new(
:user => @author,
:message => {:body => Faker::Lorem.sentence(4)},
:audio_refs => [
{:audio_track => {:id => @audio_track.id}},
{:audio_track => {:user_id => @author.id, :file => File.open("#{Rails.root}/spec/stuff/test.mp3")}}
]
)
@feed_item.save!
@feed_item.reload
@feed_item.audio_refs.length.should be(2)
end
正如您所看到的,我覆盖audio_refs =方法的原因是FeedItem可以从现有的AudioTracks(当有params [:audio_track] [:id])或上传的文件(params [:audio_track] [ :文件])
当我运行此规范时,问题是@ feed_item.audio_refs.length == 0,即不保存audio_refs。你可以帮帮我吗?
一些调查:
1)绑定参数默认为“true”(这意味着我们处于建筑模式)
答案 0 :(得分:1)
我找到了解决问题的方法,但我不明白为什么保存方法不起作用并且没有使我的代码工作。首先让我描述一下我对这个问题的调查。在调用audio_refs =之后,会创建一个audio_refs数组,但在任何audio_ref中都没有feed_item_id。可能是因为feed_item暂时没有保存。
所以解决方案非常简单 - 虚拟属性。要了解它们,请观看corresponding railscasts
所以我的解决方案是通过回调“after_save”
创建audio_refs我略微改变了我的模特:
在FeedItem.rb中我添加了
attr_writer :audio_tracks #feed_item operates with audio_tracks array
after_save :assign_audio #method to be called on callback
def assign_audio
if @audio_tracks
@audio_tracks.each do |attributes|
if attributes[:id]
self.audio_refs << AudioRef.new(:audio_track => AudioTrack.find(attributes[:id]))
elsif attributes[:file]
self.audio_refs << AudioRef.new(:audio_track => AudioTrack.new(:user_id => attributes[:user_id], :file => attributes[:file]))
end
end
end
end
规范现在是:
it "Should create audio_track and add audio_ref" do
@audio_track = Fabricate(:audio_track, :user_id => @author.id, :file => File.open("#{Rails.root}/spec/stuff/test.mp3"))
@feed_item= FeedItem.new(
:user => @author,
:message => {:body => Faker::Lorem.sentence(4)},
:audio_tracks => [
{:id => @audio_track.id},
{:user_id => @author.id, :file => File.open("#{Rails.root}/spec/stuff/test.mp3")}
]
)
@feed_item.save!
@feed_item.reload
@feed_item.audio_refs.length.should be(2)
end
它工作正常!祝你的编码好运)
答案 1 :(得分:0)
通过添加某种调试输出来检查实际上是否正在调用audio_refs=()
。我的感觉是您的FeedItem.new()
来电不使用audio_refs=()
制定者。
以下是ActiveRecord::Base#initialize
方法的源代码,取自APIdock:
# File activerecord/lib/active_record/base.rb, line 1396
def initialize(attributes = nil)
@attributes = attributes_from_column_definition
@attributes_cache = {}
@new_record = true
@readonly = false
@destroyed = false
@marked_for_destruction = false
@previously_changed = {}
@changed_attributes = {}
ensure_proper_type
populate_with_current_scope_attributes
self.attributes = attributes unless attributes.nil?
result = yield self if block_given?
_run_initialize_callbacks
result
end
我目前没有测试这个的环境,但看起来它直接设置attributes
哈希而不经过每个属性的setter。如果是这种情况,您需要手动调用您的setter。
实际上,我认为你没有得到参数数量的异常(binding
未设置)这一事实证明你的setter没有被调用。