我必须只选择属于ctype = TV
的频道的节目时间表但是我收到了这个错误:
计划(型号)
class Program < ActiveRecord::Base
has_many :program_schedules, dependent: :delete_all
ProgramSchedule (型号)
class ProgramSchedule < ActiveRecord::Base
belongs_to :program
belongs_to :channel
default_scope { includes(:channel).where("program_schedules.channels.ctype NOT IN (?)", ['tv']) }
end
频道(型号)
class Channel < ActiveRecord::Base
validates :name, :site, :ctype, :country, :presence => true
has_many :programs, :dependent => :delete_all
QUERY
@programs = Program.includes(:program_schedules).where(
"programs.ptype" => ["tv_show","movie"],
"programs.country" => @country).
select("programs.id").
group("programs.id, program_schedules.id, channels.id").
having("program_schedules.stop > ?", Time.current)
Schema.rb
create_table "channels", force: true do |t|
t.string "site"
t.string "name"
t.string "icon"
t.string "ctype"
t.string "country"
t.string "lang"
t.integer "ordertab"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "homepage"
end
create_table "programs", force: true do |t|
t.string "country"
t.string "title"
t.text "subtitle"
t.text "description"
t.float "official_rating", default: 0.0
t.float "rating", default: 0.0
t.string "ptype"
t.string "year"
t.string "imdb"
t.string "tmdb"
t.string "tvdb"
t.string "wiki"
t.string "poster"
t.string "backdrop"
t.string "trailer"
t.float "popularity", default: 0.0
end
create_table "program_schedules", force: true do |t|
t.integer "program_id"
t.datetime "start"
t.datetime "stop"
t.integer "channel_id"
end
答案 0 :(得分:2)
您的includes
应该包含关联,而不是符号形式的表名。尝试将默认范围更改为以下内容:
default_scope { includes(:channel).where("channels.ctype NOT IN (?)", ['tv']) }
答案 1 :(得分:2)
我将此作为“回答”发布,但它的目的是作为评论。我这样做是为了让我能更好地说明我的解释。如果您希望我删除此无答案,请告诉我。
我试图复制您的问题,而我根本无法使用您迄今为止发布的default_scope
查询。最终,问题是您的LEFT OUTER JOIN
条件引用了尚未加入的表。即使它在查询的后期加入,解释器也看不到那么遥远。
这是我所指的部分(为便于阅读而格式化):
...FROM "programs"
LEFT OUTER JOIN
"program_schedules"
ON "program_schedules"."program_id" = "programs"."id"
AND (channels.ctype NOT IN ...)
^^^
Problem is here. The channels table has not yet been joined.
此外,我从未看到.where
附加到连接条件。如果您正在使用该方法,ActiveRecord 应将您传递给.where
子句中的WHERE
方法的任何内容放置,如您所期望的那样。出于某种原因,显然是将LEFT OUTER JOIN
作为AND
条件附加条件。
我知道向连接添加条件的唯一方法是使用.joins
方法,如下所示:
...joins(" AND program_schedules.channels.ctype NOT IN (?)", ['tv'])
这确实会导致像这样的查询(为简洁而截断):
LEFT OUTER JOIN "program_schedules" ON ... AND program_schedules.channels......
此外,错误消息中生成的查询说明
(channels.ctype NOT IN ...)
作为失败的连接条件,而您的.where
条件状态
program_schedules.channels.ctype
我没有在你的问题的其他地方看到你在错误信息中写了任何看起来像查询部分的内容。所以,似乎我们并没有完全看到一切。
似乎我们仍然遗漏了一些信息。您是否有可能确实有类似我上面写的.join
条件?其他模型中是否有其他default_scopes
可以执行此操作?
答案 2 :(得分:1)
当我研究我在我的应用程序中看到的类似问题时,我偶然发现了这个StackOverflow问题。我认为将default_scope
与joins
一起使用只是打破了。
我将你的代码简化为一些简单的失败,运行Program.joins(:program_schedules).to_sql
并得到以下sql:
SELECT "programs".*
FROM "programs"
INNER JOIN "program_schedules" ON
"program_schedules"."program_id" = "programs"."id" AND
("channels"."ctype" NOT IN ('tv'))
问题还在于它还没有加入channels
,而是从不加入频道。 default_scope
中的where子句被添加到INNER JOIN
,但联接被丢弃了。
class Channel < ActiveRecord::Base
has_many :programs
end
class Program < ActiveRecord::Base
has_many :program_schedules
end
class ProgramSchedule < ActiveRecord::Base
belongs_to :program
belongs_to :channel
default_scope { joins(:channel).where.not(channels: { ctype: ['tv'] }) }
end
ActiveRecord::Schema.define(version: 20140331124327) do
create_table "channels", force: true do |t|
t.string "site"
t.string "name"
t.string "icon"
t.string "ctype"
t.string "country"
t.string "lang"
t.integer "ordertab"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "homepage"
end
create_table "programs", force: true do |t|
t.string "country"
t.string "title"
t.text "subtitle"
t.text "description"
t.float "official_rating", default: 0.0
t.float "rating", default: 0.0
t.string "ptype"
t.string "year"
t.string "imdb"
t.string "tmdb"
t.string "tvdb"
t.string "wiki"
t.string "poster"
t.string "backdrop"
t.string "trailer"
t.float "popularity", default: 0.0
end
create_table "program_schedules", force: true do |t|
t.integer "program_id"
t.datetime "start"
t.datetime "stop"
t.integer "channel_id"
end
end
答案 3 :(得分:1)
请查看:https://github.com/rails/rails/issues/12896
在activerecord-4.1.7 / lib / active_record / scoping / default.rb中,有下一条评论:
# If you need to do more complex things with a default scope, you can
# alternatively define it as a class method:
#
class Article < ActiveRecord::Base
def self.default_scope
# Should return a scope, you can call 'super' here etc.
end
end
这对我有用。