【问题标题】:model has_many users(of a specific role type)模型 has_many 用户(特定角色类型)
【发布时间】:2013-05-22 17:56:39
【问题描述】:

我有一个非常标准的设计用户模型架构,其中包含角色和 userRoles 的多对多模型:

  #teacher or student
  create_table "roles", :force => true do |t|
    t.string   "name"
  end

  create_table "user_roles", :force => true do |t|
    t.integer  "user_id"
    t.integer  "role_id"
  end    
  add_index "user_roles", ["role_id"], :name => "index_user_roles_on_role_id"
  add_index "user_roles", ["user_id"], :name => "index_user_roles_on_user_id"

  create_table "users", :force => true do |t|
    t.string   "email",                  :default => "", :null => false
    t.string   "encrypted_password",     :default => "", :null => false
    ........more stuff....
  end    
  add_index "users", ["email"], :name => "index_users_on_email", :unique => true
  add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true

end

角色是老师和学生。我想创建一个Clazz(如在学校班级)模型,其中包含多对多学生(具有学生角色的用户)和多对多教师(具有教师角色的用户)。

我认为它看起来像这样,但不能完全理解:

rails g scaffold Clazz name:string time:datetime has_many:users(of type student) has_many:users(of type teacher)最后两个怎么办?我是否首先创建 has_many, :through 关联迁移(teacherClass 和 studentClass)?还是有辅助方法?

也许我只是找不到合适的教程或示例,所以我也可以使用其中之一...提前致谢



这是关于我在@Cody Caughlan 的回答中尝试的更多信息

Schema.rb:

ActiveRecord::Schema.define(:version => 20130523111519) do

  create_table "class_instructions", :force => true do |t|
    t.string   "name"
    t.datetime "time"
    t.integer  "user_id"
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

  add_index "class_instructions", ["user_id"], :name => "index_class_instructions_on_user_id"

  create_table "roles", :force => true do |t|
    t.string   "name"
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

  create_table "songs", :force => true do |t|
    t.string   "title"
    t.string   "content"
    t.datetime "created_at",             :null => false
    t.datetime "updated_at",             :null => false
    t.integer  "user_id"
    t.string   "fractionRepresentation"
    t.string   "measureRepresentation"
  end

  create_table "user_roles", :force => true do |t|
    t.integer  "user_id"
    t.integer  "role_id"
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

  add_index "user_roles", ["role_id"], :name => "index_user_roles_on_role_id"
  add_index "user_roles", ["user_id"], :name => "index_user_roles_on_user_id"

  create_table "users", :force => true 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
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.string   "current_sign_in_ip"
    t.string   "last_sign_in_ip"
  end

  add_index "users", ["email"], :name => "index_users_on_email", :unique => true
  add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true

end

User.rb 模型:

class User < ActiveRecord::Base
  #DEVISE STUFF

  #== Associations
  has_many :user_roles, :dependent => :destroy
  has_many :roles, :through => :user_roles

  has_many :songs    

  #== Instance Methods
  def thisUsersID
    self.id
  end

  def student_classes(class_instruction_name)
    member_classes(class_instruction_name, 'Student')
  end

  def teacher_classes(class_instruction_name)
    member_classes(class_instruction_name, Role::TEACHER)
  end

  private

  def member_classes(class_instruction_name, type)
   ClassInstruction \
     .joins(:user_role) \
     .where(["user_role.user_id = ?", id]) \
     .joins("INNER JOIN roles ON roles.id = user_roles.role_id") \
     .where("roles.name = ?", type) \
     .where("class_instruction.name = ?", class_instruction_name)
  end
end

user_role.rb 模型:

class UserRole < ActiveRecord::Base
  belongs_to :user
  belongs_to :role
  has_many :class_instructions, :dependent => :destroy
end

role.rb 模型

class Role < ActiveRecord::Base
  has_many :user_roles, :dependent => :destroy
end

class_instruction.rb 模型(名称为“ss”和“math-101”)

class ClassInstruction < ActiveRecord::Base
  belongs_to :user_role
end

错误堆栈

Loading development environment (Rails 3.2.2)

1.9.3-p429 :001 > U = User.find(3)

  User Load (3.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 3]]
 => #<User id: 3, email: "stu@test.org", encrypted_password: "$2a$10$2DxWWV34BRFJoLboYyWCIeXEtCPYOSe8JqpTmFU6W2i1...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil> 

1.9.3-p429 :002 > U.student_classes('math-101').all

  ClassInstruction Load (0.2ms)  SELECT "class_instructions".* FROM "class_instructions" INNER JOIN "user_roles" ON "user_roles"."id" = "class_instructions"."user_role_id" INNER JOIN roles ON roles.id = user_roles.role_id WHERE (user_role.user_id = 3) AND (roles.name = 'Student') AND (class_instruction.name = 'math-101')
SQLite3::SQLException: no such column: user_role.user_id: SELECT "class_instructions".* FROM "class_instructions" INNER JOIN "user_roles" ON "user_roles"."id" = "class_instructions"."user_role_id" INNER JOIN roles ON roles.id = user_roles.role_id WHERE (user_role.user_id = 3) AND (roles.name = 'Student') AND (class_instruction.name = 'math-101')
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: user_role.user_id: SELECT "class_instructions".* FROM "class_instructions" INNER JOIN "user_roles" ON "user_roles"."id" = "class_instructions"."user_role_id" INNER JOIN roles ON roles.id = user_roles.role_id WHERE (user_role.user_id = 3) AND (roles.name = 'Student') AND (class_instruction.name = 'math-101')
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/sqlite3-1.3.7/lib/sqlite3/database.rb:91:in `initialize'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/sqlite3-1.3.7/lib/sqlite3/database.rb:91:in `new'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/sqlite3-1.3.7/lib/sqlite3/database.rb:91:in `prepare'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/connection_adapters/sqlite_adapter.rb:246:in `block in exec_query'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/connection_adapters/abstract_adapter.rb:280:in `block in log'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activesupport-3.2.2/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/connection_adapters/abstract_adapter.rb:275:in `log'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/connection_adapters/sqlite_adapter.rb:242:in `exec_query'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/connection_adapters/sqlite_adapter.rb:460:in `select'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/connection_adapters/abstract/database_statements.rb:18:in `select_all'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/connection_adapters/abstract/query_cache.rb:63:in `select_all'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/querying.rb:38:in `block in find_by_sql'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/explain.rb:40:in `logging_query_plan'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/querying.rb:37:in `find_by_sql'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/relation.rb:171:in `exec_queries'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/relation.rb:160:in `block in to_a'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/explain.rb:33:in `logging_query_plan'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/relation.rb:159:in `to_a'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/activerecord-3.2.2/lib/active_record/relation/finder_methods.rb:159:in `all'
    from (irb):2
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/railties-3.2.2/lib/rails/commands/console.rb:47:in `start'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/railties-3.2.2/lib/rails/commands/console.rb:8:in `start'
    from /Users/phycom06/.rvm/gems/ruby-1.9.3-p429/gems/railties-3.2.2/lib/rails/commands.rb:41:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'1.9.3-p429 :003 > 

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 activerecord


    【解决方案1】:

    首先,Class 是 Ruby 中的保留字,因此您不能将其用作模型名称。

    http://www.java2s.com/Code/Ruby/Language-Basics/Rubysreservedwords.htm

    所以选择一个不同的名字。在这种情况下,我已经完成了Clazz

    至于数据模型 - 是的,您将在 ActiveRecord 类定义中而不是在迁移中指定关系。

    不清楚您的Clazz 模型是什么样的。但我假设它有 2 列:nameuser_role_id。您可能希望将名称提取到单独的表中,并将其替换为 clazz_id - 但现在我们将保持不变。

    我将使用实例方法来构建关联,但您可能会在 has_many 定义中发疯。

    class Clazz < ActiveRecord::Base
      belongs_to :user_role
    end
    
    class User
    
     #== Associations
     has_many :user_roles, :dependent => :destroy
     has_many :roles, :through => :user_roles
    
     #== Instance Methods
     def student_classes(clazz_name)
      member_classes(clazz_name, Role::STUDENT)
     end
    
     def teacher_classes(clazz_name)
      member_classes(clazz_name, Role::TEACHER)
     end
    
     private
    
     def member_classes(clazz_name, type)
       Clazz \
         .joins(:user_role) \
         .where(["user_role.user_id = ?", id]) \
         .joins("INNER JOIN roles ON roles.id = user_roles.role_id") \
         .where("roles.name = ?", type) \
         .where("clazzes.name = ?", clazz_name)
     end
    
    end
    
    class UserRole
     has_many :clazzes, :dependent => :destroy
    end
    
    class Role
      has_many :user_roles, :dependent => :destroy
    end
    

    所以这将使您能够执行以下操作:

    user = User.find(99) # we know this user is a student
    user.student_classes('Social Studies 101').all
    

    类似的东西。

    【讨论】:

    • 努力工作,谢谢帮助。我将 Class 更改为 ClassInstruction。我将Role::STUDENTRole::TEACHER 更改为StudentTeacher,因为它想要的是字符串,而不是类型?我将最后一个实例方法更改为def member_classes(class_instruction_name, type) ClassInstruction \ .joins(:user_role) \ .where(["user_role.user_id = ?", id]) \ .joins("INNER JOIN roles ON roles.id = user_roles.role_id") \ .where("roles.name = ?", type) \ .where("class_instruction.name = ?", class_instruction_name) end ,但最后一个.where 不起作用并且无法使其正常工作。答案中的信息如下
    • Role::TEACHER 只是字符串,在Role 类中定义为常量。最后一个.where 不起作用,因为你的表名错误,它的复数class_instructions - 你把它当作单数。
    • class_instructionclass_instructions,它们都不起作用
    • 看起来您需要使用 user_roles 而不是 user_role - 也是单数与复数的问题。事实上,在你的 SQL 错误打击中,它基本上是这样说的:no such column: user_role.user_id:
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-12
    • 1970-01-01
    • 1970-01-01
    • 2011-04-27
    • 1970-01-01
    • 1970-01-01
    • 2020-12-29
    相关资源
    最近更新 更多