В моем приложении Rails 4 есть модель User. Он содержит перечисление для account_type

class User < ActiveRecord::Base
  enum account_type: [manager: 0, contractor: 1, employee: 2]
end

В настоящий момент account_type используется для изменения поведения нескольких действий. Но в будущем мне нужно будет представить несколько свойств, которые будут иметь разные значения для разных типов учетных записей (например, ограничения скорости для некоторых конечных точек API или цена подписки).

Как мне с этим справиться?

Я могу добавить новую модель модели UserAccountType и установить связь belongs_to - has_many между ней и моделью User, но я потеряю доступ к таким методам, как manager?, которые удобно предоставляются ActiveRecord. Также мне нужно будет соответствующим образом заполнить связанную таблицу, что означает, что мне придется поместить сопоставление «тип учетной записи» <--> целое число в seeds.rb и убедиться, что она создает записи для всех типов учетных записей, которые не являются СУХИМИ. Есть ли способ сделать это лучше?

3
S.S.J 24 Дек 2015 в 20:19

2 ответа

Лучший ответ

Похоже, вы ищете простую ролевую систему. драгоценный камень Rolify отлично подходит для этого: https://github.com/RolifyCommunity/rolify. Также легко добавить дополнительные удобные методы поверх того, что включено.

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

Если вы хотите реализовать это самостоятельно, если вы используете Postgres, тип поля массива может быть хорошим подходом для хранения и добавления «ролей». Что касается удобных методов, можно заменить что-то вроде is_manager? в несколько строк. Я бы не позволил этому удержать вас от создания более подходящей и устойчивой модели данных.

Пример:

def is_manager?
  User.find(1).roles.include?('manager')
end
3
errata 24 Дек 2015 в 19:06

Когда вы упоминаете, что хотите добавить дополнительные атрибуты к объектам enum, вы должны быть осторожны, чтобы понять, что вы хотите добавить.

Согласно документам:

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

По сути, это означает, что сохраненное значение для вашего атрибута enum будет числом; Rails «переведет» это число в его подробную форму. При желании можно легко достичь с помощью метода одного экземпляра.

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

Обратите внимание, что когда используется массив, неявное отображение значений в целые числа базы данных выводится из порядка, в котором значения появляются в массиве. В этом примере: active отображается в 0, поскольку это первый элемент, а: archived отображается в 1. Как правило, i-й элемент отображается в i-1 в базе данных.

Conversation.statuses[:active] # => 0

Conversation.statuses["archived"] # => 1

-

Разница с вашим вопросом в том, что если вы хотите добавить больше спецификации к функциональности enum, вы действительно попадаете в сферу другой модели.

Как упоминал @errata, если вы хотите использовать свой пример, вам лучше использовать ролевые функции.

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

Поэтому я бы порекомендовал посмотреть на то, чего вы пытаетесь достичь именно . Если вам нужны «дополнительные» атрибуты и т. Д., Вам понадобится другая модель. Если вы используете только отдельные значения, используйте enum.


ограничения скорости для некоторых конечных точек API или цена подписки

Я бы лично использовал что-то вроде следующего:

#app/models/user.rb
class User < ActiveRecord::Base
   belongs_to :account_type
   delegate :rate_limit, to: :account_type #-> @user.rate_limit.speed
end 

#app/models/account_type.rb
class AccountType < ActiveRecord::Base
   has_many :users
   belongs_to :rate_limit #-> maybe
end

Это позволит вам позвонить:

@user = User.find x
@user.rate_limit.throttle

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

1
Community 20 Июн 2020 в 09:12