在 Rails 中定义两个模型之间的多个(相似)关系

Defining Multiple (similar) relationships between two models in Rails

我一直在 SO 上尝试排列其他(非常)相似的答案,但还没有点击它,我觉得我很接近。

我想以两种不同的方式将用户模型与类别模型相关联,具体取决于用户希望为每个类别使用的通知渠道(文本或电子邮件)。用户可以有多个类别,反之亦然。

期望的结果:

>>@user.textcategories => #<ActiveRecord::Associations::CollectionProxy []> 类别。

>> @user.emailcategories => #<ActiveRecord::Associations::CollectionProxy []> 个类别。

>> @user.emailcategories << Category.first

>> @user.textcategories << Category.third

当前代码:

class User < ActiveRecord::Base
  has_many :emailcategories 
  has_many :categories, through: :emailcategories

  has_many :textcategories
  has_many :categories, through: :textcategories
end

class Category < ActiveRecord::Base
  has_many :emailcategories 
  has_many :users, through: :emailcategories

  has_many :textcategories 
  has_many :users, through: :textcategories
end

class Emailcategory < ActiveRecord::Base
  belongs_to :user
  belongs_to :category
end

class Textcategory < ActiveRecord::Base
  belongs_to :user
  belongs_to :category
end

migration/schema:

create_table "emailcategories" do |t|
      t.integer :user_id
      t.integer :category_id
      t.datetime "created_at",       null: false
      t.datetime "updated_at",       null: false
    end

    create_table "textcategories" do |t|
      t.integer :user_id
      t.integer :category_id
      t.datetime "created_at",       null: false
      t.datetime "updated_at",       null: false
    end

    create_table :users do |t|
      t.string :name
    end

    create_table :categories do |t|
      t.string :name
    end

当前错误信息: >> @user.emailcategories << Category.first ActiveRecord::AssociationTypeMismatch:应为电子邮件类别(#70239513207200),得到类别(#70239513077640)

Github 克隆并尝试:https://github.com/Grantimus9/TestRels

谢谢!

你做错了。你应该做如下:

@user.emailcategories.create! category: Category.first
@user.textcategories.create! category: Category.third

以上答案对于您现有的代码是正确的。但是,如果要使用 << 语法,则应在连接 table 上使用 STI。 (我也认为这是一个更简洁的解决方案,尽管这可能是个人偏好。)

# db/schema.rb
create_table "categories", force: :cascade do |t|
  t.string   "name"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

create_table "category_users", force: :cascade do |t|
  t.integer  "category_id"
  t.integer  "user_id"
  t.string   "type"
  t.datetime "created_at",  null: false
  t.datetime "updated_at",  null: false
end

create_table "users", force: :cascade do |t|
  t.string   "name"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

# app/models/user.rb
class User < ActiveRecord::Base
  has_many :email_category_users
  has_many :email_categories, through: :email_category_users, source: :category
  has_many :text_category_users
  has_many :text_categories, through: :text_category_users, source: :category

  # If you also need to get all categories on a user
  has_many :category_users
  has_many :categories, through: :category_users
end

# app/models/category_user.rb
class CategoryUser < ActiveRecord::Base
  belongs_to :user
  belongs_to :category
end

# app/models/email_category_user.rb
class EmailCategoryUser < CategoryUser
end

# app/models/text_category_user.rb
class TextCategoryUser < CategoryUser
end

# app/models/category.rb
class Category < ActiveRecord::Base
  has_many :category_users 
  has_many :users, through: :category_users
end

此解决方案允许您使用 OP 中概述的所有语法用于 creating/accessing 类别(尽管它是 @user.text_categories 等,请注意下划线)