0

我有一个应用程序,用户可以在其中自定义日历并用给定的事件池填充它。用户还可以用别名覆盖他自己的日历的标题。所以我有以下 has_many :through 关系:

class Calendar < ActiveRecord::Base
    has_many :event_aliases
    has_many :events, :through => :event_aliases
end

class Event < ActiveRecord::Base
    attr_accessible :title

    has_many :event_aliases
    has_many :calendars, :through => :event_aliases
end

class EventAliases < ActiveRecord::Base
    attr_accessible :course_id, :calendar_id, :custom_name

    belongs_to :event
    belongs_to :calendar
end

不,我想提供带有别名的日历。如果事件有别名 ( custom_name),则应该显示它。title否则应显示默认事件名称 ( )。

有没有一种方法可以轻松设置一个查询,该查询返回当前日历的所有事件,无论是custom_name(如果存在)还是使用默认值title

我当前的解决方案是将 if 条件硬编码到我想避免的查询中。

title_column = "case when custom_name IS NOT NULL then custom_name else title end as title"

# assume we are given a calendar_id
Calendar.find(calendar_id).event_aliases.joins(:event).select(title_column, :event_id).each do |event_alias|
    # do further stuff here
end

如果需要,我还可以获取所有event_aliases并遍历它们中的每一个以获取默认值title

# assume we are given a calendar_id
Calendar.find(calendar_id).event_aliases.each do |event_alias|
    title = event_alias.custom_name
    if title.nil?
        title = Event.find(event_alias.event_id).title
    # do further stuff here
end

但这给我带来了太多的查询。

那么有没有更聪明的方法来完成我想要的?也许使用命名范围或其他花哨的导轨技术?

更新

我最终通过 has_many :through 关系进行了“自定义”选择。所以唯一改变的是Calendar模型:

class Calendar < ActiveRecord::Base
    has_many :event_aliases
    has_many :events, :through => :event_aliases,
             :select => "event_aliases.custom_name as custom_name, events.*"
end

所以访问custom_name/ the titlenow 有点像@Doon 建议的那样:

Calendar.find(1).courses.each do |course|
    title = course.custom_name || course.title
end

这只会创建 2 个查询而不是 3 个:

  Calendar Load (0.6ms)  SELECT `calendars`.* FROM `calendars` WHERE `calendars`.`id` = 1 LIMIT 1
  Event Load (0.7ms)  SELECT event_aliases.custom_name as custom_name, events.* FROM `events` INNER JOIN `event_aliases` ON `events`.`id` = `event_aliases`.`event_id` WHERE `event_aliases`.`calendar_id` = 1
4

1 回答 1

1

includes在提取别名的同时使用来获取事件怎么样。

 Calendar.find(1).event_aliases.includes(:event).each do  |e| 
     puts  e.custom_name.blank? ? e.event.title : e.custom_name
 end 

SQL Rails 生成的将如下所示:

 Calendar Load (0.2ms)  SELECT "calendars".* FROM "calendars" WHERE "calendars"."id" = ?      LIMIT 1
 EventAlias Load (0.2ms)  SELECT "event_aliases".* FROM "event_aliases" WHERE "event_aliases"."calendar_id" = 1
 Event Load (0.2ms)  SELECT "events".* FROM "events" WHERE "events"."id" IN (1, 2)

另外,如果您想稍微清理一下,可以向 EventAlias 添加一个虚拟字段

class EventAlias < ActiveRecord::Base

  def name
    custom_name || self.event.title
  end

end

只要您使用包含,查询将是相同的。

于 2012-07-17T12:52:06.410 回答