Пользователи / Классные ассоциации

Я новичок в рельсах и пытаюсь создать приложение, в котором пользователи могут владеть множеством классных комнат, а в классах может быть много пользователей. Однако я не знаю, как настроить модели.

Допустим, у меня есть учитель по имени Джо. У Джо много классных комнат. В каждой из классных комнат Джо может быть много учеников, поэтому из этих множества классных комнат у Джо тоже много учеников. Но я также хочу, чтобы Джо мог быть частью класса в качестве ученика, и я хочу, чтобы ученики могли создавать свои собственные классы в качестве учителей с той же учетной записи.

В классах также должно быть более одного учителя.

Я также хочу иметь возможность делать что-то вроде user.classrooms.first.users, где пользователь - Джо, а пользователи - его ученики в его первом классе.

Какие модели мне следует создать и как установить ассоциации?

Я думал has_and_belongs_to_many, но, видимо, это больше не нравится, и has_many :through было бы лучше. Для меня это выглядит примерно так:

class User
  has_many :classroom_users
  has_many :classrooms, :through => :classroom_users
end

class Classroom
  has_many :classroom_users
  has_many :users, :through => :classroom_users
end

class Classroom_User
  belongs_to :classroom
  belongs_to :user
end

Это верно? Я взял его здесь. Кроме того, как мне отразить эти модели в базе данных с миграциями?


person liver    schedule 27.11.2015    source источник


Ответы (1)


То, что у вас есть на данный момент, будет работать в случае связывания пользователей с классами, но не учителей, поскольку на данный момент таблица для связывания двух моделей может обозначать только один тип отношений.

NB: Rails ожидает, что имена моделей будут в единственном числе и без подчеркивания, т.е. ClassroomUser вместо Classroom_Users в вашем примере.

Чтобы связать учителей с классами, можно было бы создать дополнительную модель объединения:

user.rb:

class User < ActiveRecord::Base
  has_many :classroom_teachers
  has_many :classroom_students
  has_many :teaching_classrooms, through: :classroom_teachers
  has_many :attending_classrooms, through: :classroom_students
end

classroom.rb:

class Classroom < ActiveRecord::Base
  has_many :classroom_teachers
  has_many :classroom_students
  has_many :teachers, through: :classroom_teachers
  has_many :students, through: :classroom_students
end

classroom_student.rb:

class ClassroomStudent < ActiveRecord::Base
  belongs_to :student, class_name: 'User', foreign_key: 'user_id'
  belongs_to :attending_classroom, class_name: 'Classroom', foreign_key: 'classroom_id'
end

classroom_teacher.rb:

class ClassroomTeacher < ActiveRecord::Base
  belongs_to :teacher, class_name: 'User', foreign_key: 'user_id'
  belongs_to :teaching_classroom, class_name: 'Classroom', foreign_key: 'classroom_id'
end

Rails обычно определяет, какой тип модели относится к полю, основываясь на имени поля, например поле users будет ссылаться на коллекцию моделей User. С помощью приведенной выше схемы Rails не может вывести типы моделей из имен связанных полей, поскольку он не знает, что teacher является псевдонимом для user. Чтобы преодолеть это, атрибуты class_name определяют типы модели соединяемых полей.

По той же причине Rails нуждается в некотором руководстве, чтобы знать, какой ключ базы данных относится к какому полю, для чего предназначен атрибут foreign_key.

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

class CreateClassroomUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
    end

    create_table :classrooms do |t|
    end

    create_table :classroom_students do |t|
      t.belongs_to :user, index: true
      t.belongs_to :classroom, index: true
    end

    create_table :classroom_teachers do |t|
      t.belongs_to :user, index: true
      t.belongs_to :classroom, index: true
    end
  end
end

Изменить:

В качестве альтернативы, вместо использования двух моделей объединения, вы можете добавить дополнительное поле к исходной модели ClassroomUser, описывающей роль пользователя (например, enum, который может иметь значение student или teacher). Это позволит добавить больше ролей в будущем и, возможно, будет проще запросить, чем мое предыдущее предложение. Например, чтобы проверить, является ли пользователь учеником или учителем, вам понадобится только один запрос:

example_user.classroom_users

и затем может проверить поля ролей в возвращенных ClassroomUser записях. См. Пример в этом вопросе. этого подхода.

person tmw    schedule 28.11.2015
comment
Большое спасибо! Действительно помогло! - person liver; 29.11.2015
comment
Без проблем. Я сделал небольшое изменение в конце своего сообщения, так как не уверен, что мой исходный ответ был лучшим решением для того, что вы пытаетесь смоделировать. - person tmw; 29.11.2015
comment
Если бы я хотел добавить уже существующего пользователя в уже существующий класс, как бы я это сделал? Я пробовал Classroom_Student.create!(user_id: example_user, classroom_id: example_classroom), но не работает. Вот что я получаю: Unable to autoload constant Classroom_Student, expected app/models/classroom_student.rb to define it. - person liver; 29.11.2015
comment
Вам следует избавиться от подчеркивания и использовать ClassroomStudent (см. Второй абзац моего ответа). Затем вы можете создать отношение с помощью: ClassroomStudent.create(user: example_user, classroom: example_classroom), example_user.classrooms << example_classroom или example_classroom.users << example_user. Я предполагаю, что имена полей взяты из вашего исходного примера, поэтому измените их соответствующим образом, если вы использовали имена из моего ответа. - person tmw; 29.11.2015