Прошло почти 2 месяца с тех пор, как я начал изучать рельсы для внесения некоторых модификаций в плагин Redmine на предприятии (будучи студентом, изучающим «образ жизни»), но я нигде не могу найти решение этой проблемы.

Позвольте мне объяснить:

Мне нужно было сгруппировать огромное количество записей времени по идентификатору пользователя и идентификатору проекта (с этим проблем нет), но если я сделаю это, мне нужно суммировать часы в этих группах.

Структура примерно такая:

| ID | project_id | user_id | issue_id | часы |

И эта информация создается с помощью этого метода:

def time_entries_for_user(user, options={})
    extra_conditions = options.delete(:conditions)
    return TimeEntry.select('*,sum(hours) as sum_hours').
      includes(self.includes).
      where(self.conditions([user], extra_conditions)).
      group('issue_id, time_entries.project_id').
      order('issues.id ASC')
end

Я добавил выбор, потому что мне нужен был весь столбец плюс сумма, и одна группа работает хорошо, но я не могу отобразить столбцы Sum. Это код json из результата этого метода:

{"test1 / SubProyectoTest1":{"logs":[{"id":24,"project_id":4,"user_id":1,"issue_id":10,"hours":6.0,"comments":"","activity_id":8,"spent_on":"2016-05-03","tyear":2016,"tmonth":5,"tweek":18,"created_on":"2016-05-03T11:07:09.000Z","updated_on":"2016-05-03T11:07:09.000Z","sum_hours":9.0}],"users":[{"id":1,.....

Самое забавное, что в другом методе, который группирует по проектам, появляется столбец Sum. Этот хорошо работает:

def time_entries_for_all_users(project)
    return project.time_entries.select('*,sum(hours) as sum_hours').
    includes(self.includes).
    where(self.conditions(self.users)).
    group('issue_id,user_id')
      order('issues.id ASC')
    end


{"test1 / SubProyectoTest1":{"logs":[{"id":24,"project_id":4,"user_id":1,"issue_id":10,"hours":6.0,"comments":"","activity_id":8,"spent_on":"2016-05-03","tyear":2016,"tmonth":5,"tweek":18,"created_on":"2016-05-03T11:07:09.000Z","updated_on":"2016-05-03T11:07:09.000Z","sum_hours":9.0}],"users":[{"id":1.......

В логах эта ошибка показывает:

ActionView :: Template :: Error (неопределенный метод `sum_hours 'для #)

Ссылаясь на этот код в представлении, которое отображает записи времени:

 <% when l(:field_hours) %>
<strong><%= number_with_precision(time_entry.sum_hours, :precision => @precision) %></strong>

Что ж, нельзя назвать то, чего не существует ...

2º раз просить помощи в StackOverflow, так что если вам нужна дополнительная информация, потому что я забыл ее написать, сделайте это :).

С уважением и благодарностью.

Отредактированный :

Благодаря BoraMa и Emiliano я заставил его работать, добавляя отношения вручную. Код первого образца изменен на:

    def time_entries_for_user(user, options={})
        extra_conditions = options.delete(:conditions)

        return TimeEntry.select('*,sum(hours) as sum_hours').
              includes(self.includes).
              where(self.conditions([user], extra_conditions)).
              where("time_entries.user_id",user)
              group('issue_id, time_entries.project_id').
              order('issues.id ASC')
          end
   end

Хотя он показывает только одну запись с правильной суммой часов, но я пытаюсь сгруппировать по issue_id, и он группируется по user_id, что приводит к отображению только 1 записи.

ИЗМЕНИТЬ 2: НАКОНЕЦ !! Я не понимал, что порядок методов может изменить результат: /. Окончательный код:

  def time_entries_for_user(user, options={})
    extra_conditions = options.delete(:conditions)

    return TimeEntry.group('time_entries.project_id,time_entries.issue_id').
      select('*,sum(hours) as sum_hours').
      includes(self.includes).
      where(self.conditions([user], extra_conditions)).
      where("time_entries.user_id",user)
      order('issues.id ASC')
  end

Спасибо вам, ребята!

0
Ruben Barbosa 9 Май 2016 в 15:39

2 ответа

Лучший ответ

В моих тестах выбранный вручную столбец (sum_hours в вашем случае) обычно доступен как "виртуальный" атрибут, когда вы выполняете запрос к модели, как в вашем первом примере - метод time_entries_for_user .

Однако это не сработает, если вы попытаетесь добавить настраиваемый выбор к ассоциации записи . Тогда я получаю ту же ошибку, что и вы (NoMethodError). Чтобы это начало работать, я бы посоветовал переписать ваш второй запрос (time_entries_for_all_users) в форму без использования ассоциаций , то есть примерно так:

def time_entries_for_all_users(project)
  TimeEntry.select('*,sum(hours) as sum_hours').     # <- no association used here
      includes(self.includes).
      where(self.conditions(self.users)).
      where("time_entries.project_id", project.id).  # <- the assoc. is added here instead
      group('issue_id,user_id')
      order('issues.id ASC')
end

Не то, что вместо вызова ассоциации time_entries для текущей записи project мы выбираем все временные записи, имеющие этот идентификатор проекта. Это должно вернуть те же записи, что и исходный запрос, но настраиваемые столбцы также должны работать правильно. Возможно, вам нужно будет обновить запрос дальше, чтобы другие условия, включая и другие, тоже работали нормально.

В качестве второго варианта вы всегда можете определить связь вместе с ручным выбором:

has_many :time_entries_with_hours_sum,  
         -> { select("time_entries.*, sum(hours) as sum_hours") }, class_name: "TimeEntry"

Тогда ваш настраиваемый столбец должен быть обычно доступен с помощью этой новой ассоциации:

project.time_entries_with_hours_sum.first.sum_hours
# => 9.0
1
BoraMa 9 Май 2016 в 16:12

Проблема в том, что когда вы добавляете столбец SUM таким образом и конвертируете в json, у вас не будет проблем, однако, если вы попытаетесь получить доступ к полю как к атрибуту, Rails не сможет его найти, поскольку оно не является частью модель.

Либо вы конвертируете в json объект в json в представлении, либо создаете другой метод только для этого случая и получаете только сумму (возможно, используя специальный метод sum ActiveRecord - для удобства чтения).

0
Emiliano Della Casa 9 Май 2016 в 13:55