Проектирование ActiveS для Rails - PullRequest
1 голос
/ 08 октября 2019

Мне нужна помощь в моделировании моих моделей и контроллера. Вот чего я хочу достичь:

Я хочу, чтобы у меня был пользователь с именем User (как обычно) и вторая модель с именем Project. Проект должен принадлежать одному пользователю и в то же время иметь много участников. Участниками проекта также должны быть пользователи (с разработкой регистрации / входа в систему), но пользователь, создавший проект, не должен иметь возможности участвовать. Все идет нормально. Вот небольшая часть: в моем контроллере я хочу написать:

def participate
  p = Project.find(id: params[:id])
  p.participants << current_user unless p.participants.includes?(current_user) && !p.user_id.equal(current_user.id)
  if p.save
    redirect_back
  else
    render :project
  end
end

Это не работает, потому что p.participants не является массивом и запросом (я пробовал это в консоли rails)не проверяет мою таблицу. Вот мои текущие настройки модели:

class Project < ApplicationRecord
  before_validation :set_uuid, on: :create
  validates :id, presence: true

  belongs_to :user
  has_and_belongs_to_many :participants, class_name: "User"
end
class User < ApplicationRecord
  before_validation :set_uuid, on: :create
  validates :id, presence: true

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_and_belongs_to_many :projects
end

Наконец, мои миграции:

class CreateProjects < ActiveRecord::Migration[6.0]
  def change
    create_table :projects, id: false do |t|
      t.string :id, limit: 36, primary_key: true
      t.string :title
      t.belongs_to :user, index: true, foreign_key: true, type: :uuid
      t.datetime :published_at

      t.timestamps
    end
  end
end
class CreateJoinTableProjectsUsers < ActiveRecord::Migration[6.0]
  def change
    create_join_table :users, :projects do |t|
      t.index :project_id
      t.index :user_id
    end
  end
end

1 Ответ

1 голос
/ 09 октября 2019

Лучше использовать has_many: through вместо has_and_belongs_to_many. Это позволяет вам писать более чистый код для проверки.

  1. Удалить has_and_belongs_to_many из моделей пользователей и проектов
  2. Добавить has_many: через модели пользователей и проектов

    rails g model UserProject user:references project:references
    rails db:migrate
    
    class User < ApplicationRecord
      ..
      has_many :user_projects
      has_many :projects, through: :user_projects
      ..
    end
    
    class Project < ApplicationRecord
      ..
      has_many :user_projects
      has_many :participants, through: :user_projects, source: 'user'
      ..
    end
    
    class UserProject < ApplicationRecord
      belongs_to :user
      belongs_to :project
    end
    
  3. Добавить проверку в модель UserProject

    class UserProject < ApplicationRecord
      belongs_to :user
      belongs_to :project
    
      validate :check_participant
    
      private
      def check_participant
        return if project.participants.pluck(:id).exclude?(user.id) && project.user != user
        errors.add(:base, 'You cannot be participant')
      end
    end
    
  4. Обновить метод участия

    def participate
      p = Project.find(id: params[:id])
      begin
        p.participants << current_user
        redirect_back
      rescue ActiveRecord::RecordInvalid => invalid
        puts invalid.record.errors
        render :project
      end
    end
    
...