节省! mongoid 中引用属性的方法

发布于 2024-11-15 16:08:09 字数 1997 浏览 2 评论 0原文

我使用 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] 时)或上传的文件创建(参数[:音频轨道] [:文件])。

问题是,当我运行此规范时,@feed_item.audio_refs.length == 0,即不保存audio_refs。你能帮我吗?

一些调查:

1)绑定参数默认为“true”(这意味着我们处于构建模式)

I use Rails 3.0.6 with mongoID 2.0.2. Recently I encountered an issue with save! method when overriding setter (I am trying to create my own nested attributes).

So here is the model:

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 model (which is just buffer between audio_tracks and feed_items) is:

class AudioRef
  include Mongoid::Document

  belongs_to :feed_item
  belongs_to :audio_track

end

And AudioTrack:

class AudioTrack
  include Mongoid::Document
  has_many :audio_refs
  mount_uploader :file, AudioUploader
end

So here is the spec for the FeedItem model which doesn`t work:

  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

As you can see, the reason I am overriding audio_refs= method is that FeedItem can be created from existing AudioTracks (when there is params[:audio_track][:id]) or from uploaded file (params[:audio_track][:file]).

The problem is that @feed_item.audio_refs.length == 0 when I run this spec, i.e. audio_refs are not saved. Could you please help me with that?

Some investigation:

1) binding param is "true" by default (this means we are in building mode)

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

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

发布评论

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

评论(2

沙与沫 2024-11-22 16:08:09

我找到了问题的解决方案,但我不明白为什么保存方法不起作用并且没有使我的代码工作。首先让我描述一下我对这个问题的调查。在调用 audio_refs= 后,将创建一个 audio_refs 数组,但在任何 audio_ref 中都没有 feed_item_id。可能是因为 feed_item 暂时没有保存。

所以解决方案很简单——虚拟属性。要了解它们,请观看相应的railscasts

所以我的解决方案是通过以下方式创建audio_refs回调“after_save”的

我稍微改变了我的模型:

在 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

并且它工作正常!祝你编码顺利)

I found a solution to my problem but I didnt understand why save method doesnt work and didn`t make my code work. So first of all let me describe my investigations about the problem. After audio_refs= is called an array of audio_refs is created BUT in any audio_ref is no feed_item_id. Probably it is because the feed_item is not saved by the moment.

So the solution is quite simple - Virtual Attributes. To understand them watch corresponding railscasts

So my solution is to create audio_refs by means of callback "after_save"

I slightly changed my models:

In FeedItem.rb I added

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

And the spec is now:

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

And it works fine!!! Good luck with your coding)

情深如许 2024-11-22 16:08:09

通过添加某种调试输出来检查 audio_refs=() 是否确实被调用。我的感觉是您的 FeedItem.new() 调用不使用 audio_refs=() setter。

以下是 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。

实际上,我认为您没有收到参数数量的异常(未设置绑定)这一事实证明您的设置器没有被调用。

Check that audio_refs=() is actually being called, by adding debug output of some kind. My feeling is that your FeedItem.new() call doesn't use the audio_refs=() setter.

Here's the source code of the ActiveRecord::Base#initialize method, taken from 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

I don't currently have an environment to test this, but it looks like it's setting the attributes hash directly without going through each attribute's setter. If that's the case, you'll need to call your setter manually.

Actually, I think the fact you're not getting an exception for the number of arguments (binding not set) proves that your setter isn't being called.

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