ассоциации has_one и own_to с одной и той же таблицей - PullRequest
4 голосов
/ 19 февраля 2020

Интересно, нормально ли иметь две ассоциации с одной и той же таблицей? Например:

class CreateTeams < ActiveRecord::Migration[6.0]
  def change
    create_table :teams do |t|
      t.string :name, null: false
      t.references :manager, foreign_key: { to_table: 'users' }
    end
  end
end

class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :name, null: false
      t.references :team
    end
  end
end

class Team < ApplicationRecord
  has_many :users
  belongs_to :manager, class_name: 'User', foreign_key: 'manager_id'
end

class User < ApplicationRecord
  belongs_to :team
  has_one :child_team, class_name: 'Team' # bad name, but just for example
end

Или было бы лучше создать таблицу соединения с team_id, user_id и member_type?

Версии Ruby / Rails не имеют значения, но давайте предположим, что Ruby 2.7.0 и Rails 6.0.0

Ответы [ 2 ]

4 голосов
/ 19 февраля 2020

С технической точки зрения - это прекрасно, но будьте осторожны с возможным внешним ключом l oop в будущем.

Это больше вопрос архитектуры и ваших прогнозов того, как будет развиваться система , Отношение «многие ко многим» с явной моделью соединения является более гибким. Например:

  1. всегда ли менеджер принадлежит команде? с помощью таблицы соединений легче выбрать «всех пользователей из группы, независимо от роли» или «всех команд, к которым имеет отношение человек, а также независимо от роли»
  2. , если будут другие роли или несколько человек на одной и той же позиции - вам также пригодится стол для присоединения
1 голос
/ 19 февраля 2020

То, что у вас есть, кажется нормальным.

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

class CreateTeams < ActiveRecord::Migration[6.0]
  def change
    create_table :teams do |t|
      t.string :name, null: false

      t.timestamps
    end
  end
end

class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :name, null: false

      t.timestamps
    end
  end
end

class CreateTeamMembers < ActiveRecord::Migration[6.0]
  def change
    create_table :team_members do |t|
      t.belongs_to :user, null: false, foreign_key: true
      t.belongs_to :team, null: false, foreign_key: true
      t.integer :role, null: false

      t.timestamps

      # Enforce one manager per team
      t.index [:team_id],
        name: :one_manager,
        unique: true,
        where: "role = 0"
    end
  end
end

class TeamMember < ApplicationRecord
  enum role: { manager: 0, player: 1, fan: 2 }

  belongs_to :user
  belongs_to :team
end

class Team < ApplicationRecord
  has_many :users, through: :team_members

  has_many :team_members, dependent: :destroy
  has_one :manager, -> { where(role: :manager) }, class_name: "TeamMember"
  has_many :players, -> { where(role: :player) }, class_name: "TeamMember"
  has_many :fans, -> { where(role: :fan) }, class_name: "TeamMember"  
end

class User < ApplicationRecord
  has_many :team_memberships, dependent: :destroy, class_name: "TeamMember"
  has_many :teams, through: :team_memberships
end

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

Это то, к чему вы можете перейти позже, если необходимо.

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