Ruby on Rails — разделение класса на несколько классов

У меня есть это приложение: Диспетчер задач.

У меня есть User.

  • Пользователь может создать одну или несколько Groups ->, после чего он становится администратором созданных им групп.
  • В то же время пользователь может быть просто Member (средней таблицей между пользователем и группой) группы, созданной другим пользователем.

Итак, у меня есть этот пользователь:

has_many :groups, foreign_key: :admin_id, dependent: :destroy
has_many :groups, through: :members

А теперь я хочу спросить у Дб:

  • Дайте мне группы, где пользователь является администратором
  • Дайте мне группы, где пользователь просто пользователь

Скорее всего, я смогу создать SQL-запрос для этого, но я думал, что будет больше способов работы с Rails.

Есть ли способ сделать это? Я могу думать только о том, чтобы иметь пользователя, но в то же время разделить его на 2 разных подкласса, таких как UserAdmin и NormalUser. Но я не уверен, как это сделать и правильный ли это подход.

Заранее спасибо!

А как насчет промежуточной модели (у вас, кажется, уже есть таблица member) с user_id, group_id и role. Последнее может быть 'member' или 'admin', чтобы указать роль пользователя в этой группе. Таким образом, у вас есть одна таблица для запроса, и вы можете легко иметь несколько администраторов в группе (по крайней мере, технически).

Stefan 10.02.2023 13:29
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
66
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

1. Добавьте столбец внешнего ключа

Если вы хотите иметь определенную связь между пользователем и группой, вы можете добавить отдельную ассоциацию:

class AddCreatorToGroups < ActiveRecord::Migration[7.0]                                                                   
  def change
    # note that you'll have to worry about filling this 
    # column if you have existing data                                                                                                                
    add_reference :groups, :founder, null: false, foreign_key: { to_table: :users }                                                      
  end                                                                                                                   
end 

class Group
  # ...
  belongs_to :founder, 
    class_name: 'User',
    inverse_of: :groups_as_founder
end

class User
  # ...
  has_many :groups_as_founder,
    class_name: 'Group',
    foreign_key: :founder_id,
    inverse_of: :founder
end

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

def create
  @group = Group.new(group_params) do |group|
    group.founder = current_user
  end
  # ...
end

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

2. Добавьте роли в членство

class AddCreatorToGroups < ActiveRecord::Migration[7.0]                                                                   
  def change                                                                                                                
    add_columns :memberships, :role, :integer, default: 0
  end                                                                                                                   
end 
class Membership < ApplicationRecord
  belongs_to :group
  belongs_to :member, class_name: 'User'
  enum :role, {
     normal: 0,
     admin: 1
  }
end
class Group < ApplicationRecord
   has_many :normal_memberships,
     class_name: 'Membership',
     -> { Membership.normal }
   has_many :admin_memberships,
     class_name: 'Membership',
     -> { Membership.admin }

  has_many :normal_members,
    class_name: 'User',
    through: :normal_memberships

  has_many :admin_members,
    class_name: 'User',
    through: :admin_memberships
end
class User < ApplicationRecord
   has_many :normal_memberships,
     class_name: 'Membership',
     -> { Membership.normal }
   has_many :admin_memberships,
     class_name: 'Membership',
     -> { Membership.admin }

  has_many :groups_as_normal_member,
    class_name: 'Group',
    through: :normal_memberships

  has_many :groups_as_admin_member,
    class_name: 'Group',
    through: :admin_memberships
end

Это очень упрощенный пример, в котором доступные роли определяются через перечисление в таблице членства. В более сложном примере роль может быть определена как role_id, указывающая на отдельную таблицу.

Этого повторения можно избежать, зациклившись на Membership.roles.key.

Одна вещь, которую следует учитывать, это то, что это возлагает много обязанностей на класс Membership.

3. Отдельная ролевая система.

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

Обратите внимание, что я использовал membership, а не member намеренно.

max 10.02.2023 13:49

Спасибо!! Когда вы говорите, что членство принадлежит члену, мне трудно это понять. Моя текущая структура: User -> Member <- Group, поскольку отношение между пользователем и группой: многие ко многим. Почему тогда членство принадлежит участнику и группе, а не пользователю и группе? Спасибо за удивительный ответ кстати!

josegp 10.02.2023 21:39

Это не дополнительный стол. То, что вы называете Member, должно называться Membership. В английском языке вы можете быть членом группы, но это наречие — существительное означает членство, если вы не говорите о человеке. Например, у вас будет членская карта, и это действительно то, что представляет эта таблица.

max 11.02.2023 10:09

Это дает название, которое действительно имеет смысл для носителей английского языка — пользователь has_many :memberships. И группа has_many :members, through: :memberships, class_name: 'User'.

max 11.02.2023 10:12

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