Avoid lots of CustomField.find_by_id calls when displaying an issue history with custom fields (#15072).

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@12217 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Jean-Philippe Lang 2013-10-13 10:04:59 +00:00
parent 60d6e16978
commit 7bea176cdb
5 changed files with 56 additions and 11 deletions

View File

@ -103,6 +103,7 @@ class IssuesController < ApplicationController
@journals = @issue.journals.includes(:user, :details).reorder("#{Journal.table_name}.id ASC").all
@journals.each_with_index {|j,i| j.indice = i+1}
@journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
Journal.preload_journals_details_custom_fields(@journals)
# TODO: use #select! when ruby1.8 support is dropped
@journals.reject! {|journal| !journal.notes? && journal.visible_details.empty?}
@journals.reverse! if User.current.wants_comments_in_reverse_order?

View File

@ -261,23 +261,23 @@ module IssuesHelper
values_by_field = {}
details.each do |detail|
if detail.property == 'cf'
field_id = detail.prop_key
field = CustomField.find_by_id(field_id)
field = detail.custom_field
if field && field.multiple?
values_by_field[field_id] ||= {:added => [], :deleted => []}
values_by_field[field] ||= {:added => [], :deleted => []}
if detail.old_value
values_by_field[field_id][:deleted] << detail.old_value
values_by_field[field][:deleted] << detail.old_value
end
if detail.value
values_by_field[field_id][:added] << detail.value
values_by_field[field][:added] << detail.value
end
next
end
end
strings << show_detail(detail, no_html, options)
end
values_by_field.each do |field_id, changes|
detail = JournalDetail.new(:property => 'cf', :prop_key => field_id)
values_by_field.each do |field, changes|
detail = JournalDetail.new(:property => 'cf', :prop_key => field.id.to_s)
detail.instance_variable_set "@custom_field", field
if changes[:added].any?
detail.value = changes[:added]
strings << show_detail(detail, no_html, options)
@ -320,7 +320,7 @@ module IssuesHelper
old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
end
when 'cf'
custom_field = CustomField.find_by_id(detail.prop_key)
custom_field = detail.custom_field
if custom_field
multiple = custom_field.multiple?
label = custom_field.name

View File

@ -58,9 +58,7 @@ class Journal < ActiveRecord::Base
def visible_details(user=User.current)
details.select do |detail|
if detail.property == 'cf'
field_id = detail.prop_key
field = CustomField.find_by_id(field_id)
field && field.visible_by?(project, user)
detail.custom_field && detail.custom_field.visible_by?(project, user)
elsif detail.property == 'relation'
Issue.find_by_id(detail.value || detail.old_value).try(:visible?, user)
else
@ -146,6 +144,22 @@ class Journal < ActiveRecord::Base
notified_watchers.map(&:mail)
end
# Sets @custom_field instance variable on journals details using a single query
def self.preload_journals_details_custom_fields(journals)
field_ids = journals.map(&:details).flatten.select {|d| d.property == 'cf'}.map(&:prop_key).uniq
if field_ids.any?
fields_by_id = CustomField.find_all_by_id(field_ids).inject({}) {|h, f| h[f.id] = f; h}
journals.each do |journal|
journal.details.each do |detail|
if detail.property == 'cf'
detail.instance_variable_set "@custom_field", fields_by_id[detail.prop_key.to_i]
end
end
end
end
journals
end
private
def split_private_notes

View File

@ -19,6 +19,12 @@ class JournalDetail < ActiveRecord::Base
belongs_to :journal
before_save :normalize_values
def custom_field
if property == 'cf'
@custom_field ||= CustomField.find_by_id(prop_key)
end
end
private
def normalize_values

View File

@ -155,6 +155,20 @@ class JournalTest < ActiveSupport::TestCase
assert journals.detect {|journal| !journal.issue.project.is_public?}
end
def test_preload_journals_details_custom_fields_should_set_custom_field_instance_variable
d = JournalDetail.new(:property => 'cf', :prop_key => '2')
journals = [Journal.new(:details => [d])]
d.expects(:instance_variable_set).with("@custom_field", CustomField.find(2)).once
Journal.preload_journals_details_custom_fields(journals)
end
def test_preload_journals_details_custom_fields_with_empty_set
assert_nothing_raised do
Journal.preload_journals_details_custom_fields([])
end
end
def test_details_should_normalize_dates
j = JournalDetail.create!(:old_value => Date.parse('2012-11-03'), :value => Date.parse('2013-01-02'))
j.reload
@ -176,6 +190,16 @@ class JournalTest < ActiveSupport::TestCase
assert_equal '0', j.value
end
def test_custom_field_should_return_custom_field_for_cf_detail
d = JournalDetail.new(:property => 'cf', :prop_key => '2')
assert_equal CustomField.find(2), d.custom_field
end
def test_custom_field_should_return_nil_for_non_cf_detail
d = JournalDetail.new(:property => 'subject')
assert_equal nil, d.custom_field
end
def test_visible_details_should_include_relations_to_visible_issues_only
issue = Issue.generate!
visible_issue = Issue.generate!