Как я могу сделать эту страницу Ruby on Rails более эффективной? - PullRequest
3 голосов
/ 23 декабря 2008

Я создаю сайт, где пользователи могут отслеживать свою коллекцию фигур для Dungeons & Dragons (www.ddmdb.com). Модели / отношения, включенные в эту функциональность, следующие:

Пользователь:

  • ID
  • логин (логин)
  • куча других полей

Миниатюрный:

  • ID
  • имя
  • число (# в наборе, не в счет)
  • release_id (внешний ключ)
  • куча других полей и внешних ключей

Форма собственности:

  • id (действительно ли это нужно?)
  • user_id
  • miniature_id
  • have_count
  • избранное (логическое)

Соответствующие отношения, которые я установил, таковы:

Пользователь:

  • has_many: владения
  • has_many: миниатюры,: through =>: владения,: uniq => истина,: условия => "owners.have_count> 0"
  • has_many: избранное,: through =>: владения,: источник =>: миниатюра,: uniq => истина,: условия => "владение. Фаворит = истина"

Миниатюрные

  • has_many: владения
  • has_many: владельцев,: через =>: владения,: источник =>: пользователь,: uniq => истина,: условия => "владение.have_count> 0"

Форма собственности:

  • принадлежит_: пользователь
  • принадлежит_в: миниатюре

У меня есть страница, на которой пользователь может просматривать и обновлять свою коллекцию, а также просматривать коллекции других пользователей. Он содержит список всех миниатюр на сайте и текстовое поле рядом с каждым, где пользователь может указать, сколько у них каждой миниатюры. Эта функция также существует в подсписках миниатюр (отфильтрованных по типу, выпуску, размеру, редкости и т. Д.)

Когда пользователь создает учетную запись, у него нет записей в праве собственности. Когда они используют страницу коллекции или подсписок миниатюр для обновления своей коллекции, я создаю записи в таблице владения только для миниатюр на странице отправки. Поэтому, если это полный список Коллекции, я обновляю все мини (даже если счет равен 0) или если это подсписок, я обновляю только эти миниатюры. Так что в любое время конкретного пользователя я могу иметь: - нет записей в собственности - записи для некоторых миниатюр - записи для всех миниатюр.

Проблема, с которой я столкнулся, заключается в том, что я не знаю, как запросить базу данных с помощью LEFT JOIN, используя «метод Rails», так что, если у пользователя нет записи для миниатюры в Ownerships, по умолчанию have_count = 0. В настоящее время я запрашиваю каждую комбинацию user_id / miniature_id в отдельности, поскольку перебираю все миниатюры, и это, очевидно, действительно неэффективно.

Вид:

<% for miniature in @miniatures %>
  <td><%= link_to miniature.name, miniature %></td>
  <td><%= text_field_tag "counts[#{miniature.id}]", get_user_miniature_count(current_user, miniature), :size => 2 %></td>
<% end %>

Помощник:

def get_user_miniature_count(user, miniature)
  ownerships = user.ownerships
  ownership = user.ownerships.find_by_miniature_id(miniature.id)
  if ownership.nil?
    return 0
  else
    return ownership.have_count
  end
end

Альтернативным решением было бы создание записей для всех миниатюр, когда пользователь регистрируется, но тогда мне также нужно было бы добавить 0 have_count для всех пользователей, когда новая миниатюра добавляется в базу данных после регистрации. Кажется, что это может быть немного сложным, но, возможно, это правильный путь?

Есть ли способ выполнить объединение и указать значение по умолчанию для миниатюр, если в таблице владельцев для этого конкретного пользователя нет записей?

Ответы [ 3 ]

2 голосов
/ 23 декабря 2008

Первое, что я бы сказал, это то, что модель User должна владеть кодом, который определяет, сколько из данной миниатюры принадлежит пользователю, поскольку это выглядит скорее как "бизнес-логика", а не как форматирование представления.

Я бы предложил добавить метод к вашей модели пользователя:

def owns(miniature_id)
  o = ownerships.detect { |o| o.miniature_id == miniature_id }
  (o && o.have_count) || 0
end

Сухой код, мммм.

Редактировать: обратите внимание, что владения кэшируются Rails после загрузки, и обнаружение не переопределяется ActiveRecord, как find, и действует так, как вы ожидаете, для массива (т.е. без операций с базой данных).

1 голос
/ 23 декабря 2008

Используя предложение fd и информацию, найденную на http://www.ruby -forum.com / topic / 52385 , я создал следующий метод:

def miniature_count(miniature_id)
  if @counts.nil?
    @counts = Hash.new
    ownerships.collect{|o| @counts[o.miniature_id] = o.have_count }
  end
  count = @counts[miniature_id] || 0
end

Это оказывается быстрее, чем обнаружение подхода.

Я выбрал miniature_count над владеет для имени, поскольку владеет звучит как метод, который должен возвращать логическое значение вместо целого числа.

Запрос каждой записи

Завершено в 2.61783 (0 запросов / сек) | Рендеринг: 1.14116 (43%) | БД: 1.34131 (51%) | 200 ОК [http://ddmdb/collection/1]

Методы обнаружения

Завершено в 2.20406 (0 запросов / сек) | Рендеринг: 1,87113 (84%) | DB: 0,21206 (9%) | 200 ОК [http://ddmdb/collection/1]

Метод хеширования

Завершено в 0,41957 (2 требования / сек) | Рендеринг: 0,19290 (45%) | DB: 0,10735 (25%) | 200 ОК [http://ddmdb/collection/1]

Мне обязательно нужно добавить кеширование, но это определенно улучшение. Я также подозреваю, что я преждевременно оптимизирую этот код, но это небольшой сайт, и время загрузки в 2,5 секунды меня не радует.

0 голосов
/ 23 декабря 2008

Может быть, я что-то упускаю, но способ, которым вы указали отношения, кажется достаточным для того, чтобы рельсы самостоятельно вычислили счет? Вы пробовали это?

редактирование: Повторное обсуждение в комментариях ... как насчет этого:

<% ownerships=current_user.ownerships %> 
<% for miniature in @miniatures %>
  <td><%= link_to miniature.name, miniature %></td>
  <td><%= text_field_tag "counts[#{miniature.id}]", get_miniature_count(ownerships, miniature), :size => 2 %></td>
<% end %>

Где get_miniature_count() просто перебирает предоставленные права собственности и возвращает 0 или количество, если миниатюра появляется в списке? Я думаю, что это позволит избежать повторного обращения к БД в каждой итерации 'for'.

edit 2: Я бы также предложил запустить скрипт / консоль и попытаться сделать то, что вы хотите, в ruby ​​напрямую, то есть проверить наличие миниатюр в списке владельцев, думая с точки зрения ruby, а не SQL. Часто rails и activerecord достаточно умны, чтобы выполнить необходимую «черную магию» SQL за кулисами, учитывая, что они знают отношения. Если вы найдете пользователя и затем выполните user.methods, вы увидите, что доступно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...