关系问题的未定义方法

时间:2015-06-10 05:03:37

标签: ruby-on-rails ruby postgresql

我在rails 4 app中有一个标准的has_many / belongs_to关联。

这是我的佣金代码:

task remove_faked: :environment do
   destroyed = Movie.where(fake: true).posters.where("created_at < ?",  1.minute.ago).destroy_all
   puts "Done. #{destroyed.length} records deleted."
end

但我收到错误:undefined method posters for #<Movie::ActiveRecord_Relation

我在我的控制台中检查了Poster.last,它有一个关联的movie_id和相应的电影ID。我也可以在我的应用程序中做关联,如Poster.all.where(movie_id:@ movie.id)。

4 个答案:

答案 0 :(得分:2)

movie.rb

has_many :posters, dependent: destroy

poster.rb

belongs_to :movie

任务文件

task remove_faked: :environment do
   destroyed = Movie.include(:posters).where(fake: true).where("created_at < ?",  1.minute.ago).destroy_all
   puts "Done. #{destroyed.length} records deleted."
end

答案 1 :(得分:0)

替换

destroyed = Movie.where(fake: true).posters.where("created_at < ?",  1.minute.ago).destroy_all

destroyed = Movie.includes(:posters).where("fake = ? && posters.created_at < ?", true, 1.minute.ago).references(:posters).destroy_all

这只会破坏满足特定条件的假电影。如果你使用了依赖的:: destroy选项,那么属于正在销毁的电影的海报将与电影一起销毁。

答案 2 :(得分:0)

由于您的评论似乎表明您只想销毁Poster而不是Movie s,请尝试以下操作:

class Movie < ActiveRecord::Base
  has_many :posters      
end

class Poster < ActiveRecord::Base
  belongs_to :movie

  scope :for_fake_movie, ->{ joins(:movie).where(movie:{fake: true}) }
  scope :since,->(time_frame){where("created_at < ?", time_frame)}
end

然后任务看起来像这样

task remove_faked: :environment do
   destroyed = Poster.for_fake_movie.since(1.minute.ago).destroy_all
   puts "Done. #{destroyed.length} records deleted."
end

作用域使代码更清晰,有助于将业务逻辑移动到模型中,而不是使控制器,视图和rake任务混乱。如果你想删除电影,我会选择

class Movie < ActiveRecord::Base
  has_many :posters, dependent: :destroy 

  scope :fake, ->{where(fake: true) }
  scope :posters_since, ->(time_frame){joins(:posters).where("posters.created_at < ?", time_frame)}
end

class Poster < ActiveRecord::Base
  belongs_to :movie
end

然后任务将是

task remove_faked: :environment do
   destroyed = Movie.fake.posters_since(1.minute.ago).destroy_all
   puts "Done. #{destroyed.length} records deleted."
end

答案 3 :(得分:0)

where会生成一组对象。您的posters关联是在对象本身上定义的,因此不能以这种方式应用。

我宁愿深入研究有关RDBMS如何工作的一些细节。

首先,我遵循一个简单的规则:查询要接收的类实例。您需要Poster,但是您正在查询Movie。我会反过来查询Poster

您的数据库中有一个强大的 join 概念,显然,它会在特定条件下将两个表中的行连接在一起。你可以想到它,因为它需要每对行,检查条件,如果它是真的,将组合放入内部缓冲区。我们仍然只选择海报&#39;领域,但现在我们可以使用电影&#39;字段也是:

Poster.joins(:movie) # singular, as in association, belongs_to in this case

这改变了......到目前为止没有。但现在它允许你根据相关电影在海报上施加一些条件。

merge这样做,它应用范围,甚至可能来自不同的模型。没有会产生无效SQL的连接。不是这个时候!

Poster.joins(:movie).merge(Movie.where(fake: true))

我们在这里得到的是属于fake: true电影的海报。哇,嗯?不只是把自己的条件打到它上面做你的事情:

Poster.joins(:movie)
      .merge(Movie.where(fake: true))
      .where("created_at < ?",  1.minute.ago)
      .destroy_all