ruby-on-rails - Rails ActiveModel 使用单类设计 belongs_to 和 has_many
问题描述
我需要一些帮助来为我的模型和控制器建模。这是我想要实现的目标:
我想要一个名为 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 控制台中尝试过)没有检查我的 n:m 表。这是我当前的模型设置:
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
解决方案
最好使用 has_many: through 而不是 has_and_belongs_to_many。这使您可以编写更简洁的代码进行验证。
- 从用户和项目模型中删除 has_and_belongs_to_many
添加 has_many :through 到用户和项目模型
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
向 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
更新参与方式
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
推荐阅读
- git - 无法验证签名证书或其链。证书科目
- r - 我们如何从 R 中的数据框中删除标签
- android - 如何设置textView不可点击
- jquery - 为什么我的数组在设置为输入值后会变成以“,”分隔的字符串?
- python - 尝试编写一个程序,在其中搜索单词中包含的字符串的所有单个字母
- javascript - javascript: onclick="scrollWin()" 不能按预期工作
- python-3.x - NameError:名称“face_cascade”未定义
- r - S3 方法:扩展 ggplot2 `+.gg` 函数
- ruby - 使用 Ruby 解析和显示 XML
- google-app-engine - 将防火墙添加到 IAP 保护的 App Engine 应用程序,其中成员:allUsers 被授予“IAP 保护的 Web 应用程序用户”