Наследование нескольких таблиц против наследования отдельных таблиц в Ruby on Rails

StackOverflow https://stackoverflow.com/questions/1634668

Вопрос

Последние несколько часов я бился над тем, каким путем мне идти. У меня есть модель уведомлений. До сих пор я использовал столбец messages_type для управления типами, но я думаю, что было бы лучше создать отдельные классы для типов уведомлений, поскольку они ведут себя по-разному.

В настоящее время существует 3 способа отправки уведомлений: SMS, Twitter, электронная почта

Каждое уведомление будет иметь:

id
subject
message
valediction
sent_people_count
deliver_by
geotarget
event_id
list_id
processed_at
deleted_at
created_at
updated_at

Похоже, что STI - хороший кандидат, верно? Конечно, у Twitter / SMS не будет темы, а у Twitter не будет send_people_count, valediction. Я бы сказал, что в этом случае они разделяют большинство своих полей. Однако что, если я добавлю " reply_to " поле для твиттера и логическое значение для DM?

Моя точка зрения заключается в том, что сейчас STI имеет смысл, но разве это тот случай, когда я могу надрать себя в будущем, а не просто начать с MTI?

Чтобы еще больше усложнить ситуацию, мне нужна модель информационного бюллетеня, которая является своего рода уведомлением, но разница в том, что она не будет использовать event_id или delivery_by.

Я мог видеть все подклассы уведомлений, используя около 2/3 полей базового класса уведомлений. Является ли ИППП легкой задачей, или я должен использовать MTI?

Это было полезно?

Решение

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

Ключевой вопрос: есть ли в вашем приложении места, где вы хотите рассмотреть все типы уведомлений вместе? Если это так, то это сильный признак того, что вы хотите придерживаться ИППП.

Другие советы

Рассматривали ли вы смешанный модельный подход?

Где вы используете единую таблицу наследования для основных полей уведомлений. Затем выгрузите все уникальные элементы в конкретные таблицы / модели, принадлежащие / имеющие одно отношение с вашими подклассами уведомлений.

Это немного сложнее в настройке, но получается довольно СУХОЙ, как только все классы и таблицы определены. Похоже, довольно эффективный способ хранения вещей. При энергичной загрузке вы не должны вызывать чрезмерную нагрузку на базу данных.

Для целей этого примера давайте предположим, что электронные письма не имеют уникальных деталей. Вот как это выглядит.

class Notification < ActiveRecord::Base
  # common methods/validations/associations
  ...

  def self.relate_to_details
    class_eval <<-EOF
      has_one :details, :class_name => "#{self.name}Detail"
      accepts_nested_attributes_for :details
      default_scope -> { includes(:details) }
    EOF
  end
end

class SMS < Notification
  relate_to_details

  # sms specific methods
  ...
end

class Twitter < Notification
  relate_to_details

  # twitter specific methods
  ...
end

class Email < Notification

  # email specific methods
  ...
end

class SMSDetail < ActiveRecord::Base
  belongs_to :SMS, :class_name => "SMS"           

  # sms specific validations
  ...
end

class TwiterDetail < ActiveRecord::Base
  belongs_to :twitter

  # twitter specific validations
  ...
end

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

  

Это приятно знать, но ты думаешь, что это необходимо?

Очень мало вещей, необходимых с точки зрения дизайна. По мере того, как стоимость процессора и места для хранения снижается, возникают и необходимые концепции проектирования. Я предложил эту схему, потому что она обеспечивает лучшее как от ИППП, так и от ИМТ, и устраняет некоторые из их недостатков.

Что касается преимуществ:

Эта схема обеспечивает последовательность ИППП. С таблицами, которые не нужно воссоздавать. Связанная таблица обходит десятки столбцов, в 75% ваших строк пустых. Вы также получаете легкое создание подкласса. Где вам нужно только создать соответствующую таблицу подробностей, если ваш новый тип не полностью покрыт базовыми полями уведомлений. Он также продолжает перебирать все уведомления.

В MTI вы получаете экономию хранилища и простоту настройки в соответствии с потребностями класса без необходимости переопределять одни и те же столбцы для каждого нового типа уведомлений. Только уникальные.

Однако эта схема также несет в себе главный недостаток ИППП. Таблица заменит 4. Что может привести к замедлению, как только оно станет огромным.

Короткий ответ: нет, такой подход не нужен. Я считаю, что это самый СУХОЙ способ эффективного решения проблемы. В кратчайшие сроки STI - способ сделать это. В долгосрочной перспективе MTI - это путь, но мы говорим о моменте, когда вы нажимаете миллионы уведомлений. Этот подход является хорошей промежуточной точкой, которую легко расширить.

Подробный камень

Я создал драгоценный камень для вашего решения: https://github.com/czaks/detailed. Используя его, вы можете упростить свой класс уведомлений до:

class Notification < ActiveRecord::Base
  include Detailed
end

Остальное идет прежним путем.

В качестве дополнительного бонуса вы теперь можете обращаться (читать, писать, связывать) к атрибутам, относящимся к подклассу, напрямую: tification.phone_number , не прибегая к: messages.details.phone_number . Вы также можете написать весь код в основных классах и подклассах, оставив модель Details пустой. Вы также сможете выполнять меньше запросов (в приведенном выше примере 4 вместо N + 1) для больших наборов данных, используя Notification.all_with_details вместо обычного Notification.all .

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

Я знаю, что это старо, но после того, как я нашел решение, я вижу потенциальные ответы, которые можно использовать везде! Недавно я разработал многообещающий проект для реализации множественного наследования таблиц и классов в Rails. Я провел несколько дней, подвергая его быстрой разработке, исправлениям, комментированию и документированию, и переиздал его в качестве наследования классов CITIER и встраивания наследования таблиц для Rails.

Я думаю, что это должно позволить вам делать то, что вам нужно, просто создавая модели, в которых Twitter, Email и SMS наследуют от Notification. Затем перенесите для уведомлений только общие атрибуты, а для трех подтипов - их уникальные атрибуты.

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

Попробуйте взглянуть на это: https://github.com/PeterHamilton/citier

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

Пожалуйста, убедитесь, что вы регулярно обновляете, потому что, как я уже сказал, он улучшается / развивается с каждым днем.

has_one :details, :class_name => "#{self.class.name}Detail"

не работает. self.class.name в контексте определения класса является 'Class', поэтому: class_name всегда 'ClassDetail'

Так и должно быть:

has_one :details, :class_name => "#{self.name}Detail"

Но очень хорошая идея!

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top