ActiveRecord не создаст правильный класс, используя STI

Я использую наследование одной таблицы в своем приложении и сталкиваюсь с проблемами при создании унаследованных пользователей от предка. Например, со следующей настройкой:

class School < ActiveRecord::Base

  has_many :users

end

class User < ActiveRecord::Base


  attr_accessible :type #etc...

  belongs_to :school

end

Class Instructor < User

   attr_accessible :terms_of_service
   validates :terms_of_service, :acceptance => true

end


Class Student < User

end

Как создать запись instructor или student из экземпляра School? Попытка что-то вроде School.first.instructors.build(....) дает мне только новый экземпляр User, и у меня не будет доступа к полям, специфичным для инструктора, таким как terms_of_service, вызывающим ошибки позже при создании форм для инструктора, сборка из консоли даст ошибка массового назначения (поскольку он пытается создать запись Пользователь, а не запись Инструктор, как указано). Я привел пример School, но есть несколько других ассоциаций, которые я хотел бы унаследовать из таблицы User, чтобы мне не приходилось повторять код или поля в базе данных. У меня возникла эта проблема, потому что ассоциации не могут быть разделены в настройке STI?


person Noz    schedule 07.03.2013    source источник
comment
Ассоциации разделяются в отношениях STI, но школа об этом не знает, вам нужно внедрить новую для школы, как в следующем ответе.   -  person Valery Kvon    schedule 08.03.2013
comment
Пробовал, нужно ли добавлять какие-либо дополнительные поля в базу данных, чтобы этот подход работал? (Что как бы противоречит цели ИППП)   -  person Noz    schedule 08.03.2013
comment
Нет, просто правильно настройте STI: со строкой типа данных поля 'type', перезагрузите среду. Он должен работать. Если нет, предоставьте нам больше кода, чтобы понять, что не так.   -  person Valery Kvon    schedule 08.03.2013


Ответы (3)


Вы должны указать инструкторов явно

class School < ActiveRecord::Base

  has_many :users
  has_many :instructors,:class_name => 'Instructor', :foreign_key => 'user_id'

end
person Pavel S    schedule 07.03.2013
comment
Не сработало, я добавил этот код и запустил School.last.instructors.build(:terms_of_service => true) и получил ошибку массового присваивания, потому что он все еще думает, что это запись пользователя. - person Noz; 08.03.2013
comment
@Cyle Попробуйте также указать тип при сборке School.first.instructors.build(type: 'Instructor') - person Pavel S; 08.03.2013
comment
@Cyle Вы также можете попробовать сказать user = School.first.instructors.build(....); user.becomes('Instructor') - person Pavel S; 08.03.2013
comment
Хм, ваш последний фрагмент выдает ошибку NoMethodError: undefined method 'new' for "Instructor":String - person Noz; 08.03.2013
comment
@Cyle извини user.becomes(Instructor) - person Pavel S; 08.03.2013
comment
Кажется, это сработало, но тогда мне придется вручную назначать каждое поле инструктора отдельно в моем контроллере, что не совсем желательно. Разве нет способа присвоить хэш пользовательской переменной? - person Noz; 08.03.2013
comment
Ребята, у вас нет этих вещей, и вам не нужно :class_name =› 'Instructor', :foreign_key =› 'user_id'. Почему :user_id? Внешний ключ для этой ассоциации должен быть :school_id. Другими словами, просто has_many :instructors. и это все. - person Valery Kvon; 08.03.2013
comment
@ValeryKvon Я чувствую, что это тоже должно быть так просто. Я внес эти изменения, но они все еще не совсем работают. Я запустил это в консоли School.last.instructors, и он выполнил этот SQL SELECT "users".* FROM "users" WHERE "users"."school_id" = 1 AND "users"."type" = 'instructor', почему он ищет тип в нижнем регистре? Разве это не должно быть AND "users"."type" = 'Instructor'? Может быть, это ошибка ActiveRecord? - person Noz; 08.03.2013
comment
Да, это должно быть с большой буквы. Хорошо, давайте проверим: Instructor.new(:terms_of_service => 'agreed'), User.new(:terms_of_service => 'agreed'), School.new.users.build(:terms_of_service => 'agreed'), School.new.instructors.build(:terms_of_service => 'agreed') - person Valery Kvon; 08.03.2013
comment
И у вас есть странная инструкция SQL. Rails 2 или более должен генерировать что-то вроде этого: "users"."type" IN ('Instructor') - person Valery Kvon; 08.03.2013
comment
Я использую Postgresql, может поэтому? Если я делаю Instructor.new, он устанавливает тип как Instructor с заглавной буквы. - person Noz; 08.03.2013
comment
Я тоже использую Postgres, проблема в другом. - person Valery Kvon; 08.03.2013
comment
Хорошо, я решил проблему. Пришлось удалить ассоциацию has_many :users. Я проголосовал за ответ @ValeryKvon и за этот, поскольку они оба были довольно близки. - person Noz; 08.03.2013
comment
Это отстой, он должен работать с обоими ) Проблема в другом ) - person Valery Kvon; 08.03.2013
comment
Возможно, в другой версии The Brand New Rails 3.3.15 есть пара дурацких ошибок. Rails действительно нуждается в хорошем управлении CoreDev... - person Valery Kvon; 08.03.2013

И что еще:

class School < ActiveRecord::Base
  has_many :users
  has_many :instructors
end

class Instructor < User 
  attr_accessible :terms_of_service # let it be at the first place. :)

  validates :terms_of_service, :acceptance => true
end
person Valery Kvon    schedule 07.03.2013

ОК, кажется, часть проблемы возникла из-за старой ассоциации users внутри моей модели School. Удаление этого и добавление ассоциаций для студентов и преподавателей по отдельности сработало.

Обновлен School.rb:

class School < ActiveRecord::Base

  #removed:
  #has_many :users this line was causing problems

  #added
  has_many :instructors
  has_many :students

end
person Noz    schedule 07.03.2013