Каковы недостатки использования методов модели для констант вместо столбцов Active Record в Rails? - PullRequest
3 голосов
/ 12 июля 2020

Я создаю приложение Rails, в котором будет очень большое количество моделей, использующих наследование одной таблицы. Для подклассов модели я хочу иметь возможность устанавливать постоянные значения для таких вещей, как name и description. Я знаю, что могу установить значения по умолчанию, используя attribute, например:

class SpecialWidget < Widget
  attribute :name, :string, default: "Special Widget"
  attribute :description, :text, default: "This is an awfully important widget."
end

Преимущество здесь, насколько я понимаю, заключается в том, что, сохраняя значения по умолчанию в базе данных, я сохраняю возможность использовать что-то #order для сортировки по имени и разбивки на страницы. Но вот так хранить константы в базе данных кажется плохим. Кажется, лучше использовать постоянные методы, например:

class SpecialWidget < Widget
  def name
    "Special Widget"
  end

  def description
    "This is an awfully important widget."
  end
end

Фактически, это то, что я делал изначально, но затем я прочитал такие сообщения ( one , two , three ), в котором указывалось, что тогда, если бы я хотел делать приятные вещи, такие как сортировка по методам, мне пришлось бы загрузить весь Widget.all в память, а затем выполнить простую- old Ruby sort.

Мое приложение в значительной степени построено на этих моделях STI, и мне определенно придется сортировать по константам, например name. Являются ли опасения по поводу сортировки и нумерации страниц серьезными недостатками, которые заставят меня пожалеть об использовании методов в будущем, или разница будет незначительной? Какие еще недостатки / проблемы могут быть у меня? Я бы очень хотел иметь возможность использовать методы вместо хранения констант в базе данных, если это возможно, без снижения производительности моего приложения.

Ответы [ 2 ]

2 голосов
/ 13 июля 2020

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

class SpecialWidget < Widget
  DefaultAttrs = {name: 'Special Widget', description: 'This is... etc'}
end

class Widget < ApplicationRecord
  def self.sort_by_name
    types = pluck(:type).uniq
    case_statements = types.map{|type| "WHEN '#{type}` THEN `#{type.constantize.const_get(:'DefaultAttrs')[:name]}'"
    case_sql = "CASE type #{case_statements.join(' ') END"
    order(case_sql)
  end
end

... не очень элегантно, но выполняет свою работу!

может быть, лучше поместите константы в базу данных!

1 голос
/ 13 июля 2020

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

Просто поместите данные в реляционную базу данных

База данных - это хранилище всех вещей в вашем домене, которое является динамическим c и должно сохраняться, т.е. состояние. Он должен быть внутренне согласованным, содержательно информативным и хорошо структурированным, чтобы в полной мере использовать возможности реляционной базы данных для гибкого управления и представления сложных взаимосвязанных данных.

На основе того, что вы сказали , и предполагая, что существует множество различных «типов виджетов», реализованных с использованием реализации Rail STI со столбцом type, я бы смоделировал Widget и SpecialWidget в базе данных следующим образом:

widgets
id | type
-------------------
 1 | 'Widget'
 2 | 'SpecialWidget'
 3 | 'Widget'
 4 | 'Widget'

widget_types
type            | name             | description
--------------------------------------------------------------
'Widget'        | 'Normal Widget'  | 'A normal widget.'
'SpecialWidget' | 'Special Widget' | 'This is an awfully important widget.'

Вы назвали эти значения «константами», но действительно ли они ? В целях вашего домена, будут ли они никогда не измениться, как значение Matth::PI никогда не меняется? Или описания будут изменены, виджеты переименованы, виджеты добавлены, а срок действия виджетов истек? Не зная наверняка, я собираюсь предположить, что они на самом деле не Constant.

Наличие name и description в качестве методов эффективно сохраняет эту widget_types таблицу в исходном коде вашего приложения, перемещая data out вашей базы данных. Если вы действительно не можете позволить себе лишнюю миллисекунду, которую несет простой JOIN для двух небольших строк на каждой Widget, тогда просто загрузите полную таблицу widget_types в кеш один раз при запуске приложения, и она будет работать так же, как сохранение его в исходном коде.

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

Опять же: в базе данных хранятся структурированные данные с целью гибкого управления по запросу - вы можете составлять запросы на лету, а DB может ответить на него.

I Действительно Не хочу помещать данные в базу данных

Хорошо ... тогда вам придется передать эти данные в базу данных каждый раз, когда вы хотите с ней работать. Вы можете сделать это так:

SELECT w.id, w.type, wt.name
FROM widgets w
INNER JOIN (
  VALUES ('Widget', 'Normal Widget'), ('SpecialWidget', 'Special Widget')
) wt(type, name) ON wt.type = w.type
ORDER BY wt.name

Выражение VALUES создает специальную таблицу c, сопоставляющую класс с именем. Передав это сопоставление и присоединяясь к нему (каждый раз), вы можете указать БД ORDER BY это.

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