Rails: несколько «типов» одной модели через связанные модели? - PullRequest
3 голосов
/ 26 мая 2010

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

У меня также есть много разных типов пользователей в моей системе, включая торговых агентов, клиентов, гостей и т. Д.

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

Итак, что бы я хотел, это:

User
-- first name
-- last name
-- email
--> is a "client", so
---- client field 1
---- client field 2
---- client field 3

User
-- first name
-- last name
-- email
--> is a "sales agent", so
---- sales agent field 1
---- sales agent field 2
---- sales agent field 3

and so on...

Кроме того, когда новый пользователь регистрируется, я хочу, чтобы этому новому пользователю автоматически была назначена роль «клиента» (я говорю здесь о полях базы данных, а не об авторизации, хотя я надеюсь в конечном итоге включить эту логику моя авторизация пользователя тоже). У меня есть мастер многоэтапной регистрации, который я пытаюсь собрать с помощью wizardly . Первый шаг прост, так как я просто вызываю поля, включенные в базовую модель User (например, first_name и email), но второй шаг сложнее, так как он должен вызывать поля из связанных модель (как - в моем примере выше - модель client с полями client_field_1 или client_field_2, как если бы эти поля были частью User).

Это имеет смысл? Дайте мне знать, если это не совсем понятно, и я постараюсь объяснить это по-другому.

Может кто-нибудь помочь мне с этим? Как бы я это сделал?

Ответы [ 2 ]

3 голосов
/ 30 мая 2010

STI, вероятно, хорошо подходит для ваших требований, как рекомендует tadman, если вы используете ActiveRecord (из Rails 3 легко изменить ORM). Основная информация доступна на странице документации AR , но вот некоторая дополнительная информация w.r.t. Ваша цель:

  • Определить одну модель на файл. В противном случае есть некоторые проблемы с инициализацией. Предполагая, что Клиент наследует от пользователя все в одном файле, объекты клиента не могут быть созданы, пока конструктор пользователя не был вызван один раз. Один файл на модель обходит проблему.
  • Все атрибуты в иерархии определены в верхнем классе в один прием. Это из-за проблем с производительностью, но, похоже, беспокоит многих людей в блогах. Короче говоря, код Ruby является объектно-ориентированным и должным образом инкапсулирует атрибуты. БД содержит все в одной таблице с дополнительным столбцом «тип», чтобы различать их место в иерархии. Это только «хитрость» для представления деревьев наследования в реляционных базах данных. Мы должны знать, что сопоставление ORM не является простым. Изображение на этом сайте от Мартина Фаулера может помочь понять ситуацию.

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

user = Client.new
# Do something to user
user.save
#=> <Client id: 1, name: "Michael Bolton", email: "mike@bolton.net", created_at: "2010-05-30 03:27:39", updated_at: "2010-05-30 03:27:39">

Все вышесказанное все еще действует в Rails 3 при использовании ActiveRecords.

1 голос
/ 26 мая 2010

Похоже, у вас есть два разумных подхода, но это будет зависеть от нюансов ваших требований.

Вы можете использовать Single Table Inheritance (STI), чтобы делать то, что вы хотите, где User является только базовым классом для других с именем SalesAgent или Client и так далее. Каждый из этих подклассов может определять свои собственные проверки. Все, что вам нужно для этого, - это строковый столбец с именем «type», а ActiveRecord сделает все остальное:

class User < ActiveRecord::Base
end

class Agent < User
end

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

class User < ActiveRecord::Base
  has_one :agent_role,
    :dependent => :destroy
end

class AgentRole < ActiveRecord::Base
  belongs_to :user

   # Represents the agent-specific role fields
end

Преимущество этого состоит в том, что можно использовать несколько ролей, а если вы используете has_many, то несколько ролей одного типа.

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