Rails 单个到 table 多个模型
Rails single through table for multiple models
每当我尝试在 user_grade table 中添加任何用户的角色(比如管理员)和成绩时,它就会出错,其他角色必须存在(教师、学生、监护人)。我不知道如何解决这个问题,因为这是有点复杂的关系结构。
或者有人可以建议更好的方法吗?
class Admin < User
# can post many posts
has_many :posts, dependent: :destroy , foreign_key: 'user_id'
# admin post can have many tags
has_many :post_tags, dependent: :destroy, foreign_key: 'user_id'
has_many :tags , through: :post_tags
# can have many grades to see other grades posts
has_many :user_grades, foreign_key: 'user_id'
has_many :grades, through: :user_grades
end
class Teacher < User
# can post many posts
has_many :posts, dependent: :destroy , foreign_key: 'user_id'
# teacher post can have many tags
has_many :post_tags, dependent: :destroy, foreign_key: 'user_id'
has_many :tags , through: :post_tags
# can teach many grades
has_many :user_grades, dependent: :destroy, foreign_key: 'user_id'
has_many :grades , through: :user_grades
end
class Student < User
# a student can only in particular grade
has_one :user_grade , dependent: :destroy, foreign_key: 'user_id'
has_one :grade , through: :user_grade
end
class Guardian < User
# parents can have many children in different classes
has_many :user_grades, dependent: :destroy, foreign_key: 'user_id'
has_many :grades, through: :user_grades
end
class Grade < ApplicationRecord
has_many :user_grades, dependent: :destroy
has_many :admins, through: :user_grades
has_many :teachers, through: :user_grades
has_many :students , through: :user_grades
has_one :guardian, through: :user_grade
end
class UserGrade < ApplicationRecord
belongs_to :grade
belongs_to :admin
belongs_to :teacher
belongs_to :student
belongs_to :guardian
end
数据库:
ActiveRecord::Schema.define(version: 20180410102940) do
create_table "grades", force: :cascade do |t|
t.integer "cls"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "post_tags", force: :cascade do |t|
t.integer "user_id"
t.integer "post_id"
t.integer "tag_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "posts", force: :cascade do |t|
t.integer "user_id", null: false
t.string "title"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "roles", force: :cascade do |t|
t.string "name"
t.string "resource_type"
t.integer "resource_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id"
t.index ["name"], name: "index_roles_on_name"
t.index ["resource_type", "resource_id"], name: "index_roles_on_resource_type_and_resource_id"
end
create_table "tags", force: :cascade do |t|
t.string "tag_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "user_grades", force: :cascade do |t|
t.integer "user_id"
t.integer "grade_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.integer "failed_attempts", default: 0, null: false
t.string "unlock_token"
t.datetime "locked_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "type"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true
end
create_table "users_roles", id: false, force: :cascade do |t|
t.integer "user_id"
t.integer "role_id"
t.index ["role_id"], name: "index_users_roles_on_role_id"
t.index ["user_id", "role_id"], name: "index_users_roles_on_user_id_and_role_id"
t.index ["user_id"], name: "index_users_roles_on_user_id"
end
end
我在这里使用了 rolify 和 cancancan gem 作为用户角色。
尝试将 optional
参数添加到 belongs_to
关系
class UserGrade < ApplicationRecord
belongs_to :grade
belongs_to :admin, optional: true
belongs_to :teacher, optional: true
belongs_to :student, optional: true
belongs_to :guardian, optional: true
end
Rails 5 默认需要 belongs_to 关联。我们可以通过添加 optional: true
.
来避免对 belongs_to
关系进行验证
或
您可以通过将 belongs_to_required_by_default
的值保持为 false 来关闭所有模型的此行为。
# file => config/application.rb
config.active_record.belongs_to_required_by_default = false
每当我尝试在 user_grade table 中添加任何用户的角色(比如管理员)和成绩时,它就会出错,其他角色必须存在(教师、学生、监护人)。我不知道如何解决这个问题,因为这是有点复杂的关系结构。 或者有人可以建议更好的方法吗?
class Admin < User
# can post many posts
has_many :posts, dependent: :destroy , foreign_key: 'user_id'
# admin post can have many tags
has_many :post_tags, dependent: :destroy, foreign_key: 'user_id'
has_many :tags , through: :post_tags
# can have many grades to see other grades posts
has_many :user_grades, foreign_key: 'user_id'
has_many :grades, through: :user_grades
end
class Teacher < User
# can post many posts
has_many :posts, dependent: :destroy , foreign_key: 'user_id'
# teacher post can have many tags
has_many :post_tags, dependent: :destroy, foreign_key: 'user_id'
has_many :tags , through: :post_tags
# can teach many grades
has_many :user_grades, dependent: :destroy, foreign_key: 'user_id'
has_many :grades , through: :user_grades
end
class Student < User
# a student can only in particular grade
has_one :user_grade , dependent: :destroy, foreign_key: 'user_id'
has_one :grade , through: :user_grade
end
class Guardian < User
# parents can have many children in different classes
has_many :user_grades, dependent: :destroy, foreign_key: 'user_id'
has_many :grades, through: :user_grades
end
class Grade < ApplicationRecord
has_many :user_grades, dependent: :destroy
has_many :admins, through: :user_grades
has_many :teachers, through: :user_grades
has_many :students , through: :user_grades
has_one :guardian, through: :user_grade
end
class UserGrade < ApplicationRecord
belongs_to :grade
belongs_to :admin
belongs_to :teacher
belongs_to :student
belongs_to :guardian
end
数据库:
ActiveRecord::Schema.define(version: 20180410102940) do
create_table "grades", force: :cascade do |t|
t.integer "cls"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "post_tags", force: :cascade do |t|
t.integer "user_id"
t.integer "post_id"
t.integer "tag_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "posts", force: :cascade do |t|
t.integer "user_id", null: false
t.string "title"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "roles", force: :cascade do |t|
t.string "name"
t.string "resource_type"
t.integer "resource_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id"
t.index ["name"], name: "index_roles_on_name"
t.index ["resource_type", "resource_id"], name: "index_roles_on_resource_type_and_resource_id"
end
create_table "tags", force: :cascade do |t|
t.string "tag_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "user_grades", force: :cascade do |t|
t.integer "user_id"
t.integer "grade_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.integer "failed_attempts", default: 0, null: false
t.string "unlock_token"
t.datetime "locked_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "type"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true
end
create_table "users_roles", id: false, force: :cascade do |t|
t.integer "user_id"
t.integer "role_id"
t.index ["role_id"], name: "index_users_roles_on_role_id"
t.index ["user_id", "role_id"], name: "index_users_roles_on_user_id_and_role_id"
t.index ["user_id"], name: "index_users_roles_on_user_id"
end
end
我在这里使用了 rolify 和 cancancan gem 作为用户角色。
尝试将 optional
参数添加到 belongs_to
关系
class UserGrade < ApplicationRecord
belongs_to :grade
belongs_to :admin, optional: true
belongs_to :teacher, optional: true
belongs_to :student, optional: true
belongs_to :guardian, optional: true
end
Rails 5 默认需要 belongs_to 关联。我们可以通过添加 optional: true
.
belongs_to
关系进行验证
或
您可以通过将 belongs_to_required_by_default
的值保持为 false 来关闭所有模型的此行为。
# file => config/application.rb
config.active_record.belongs_to_required_by_default = false