Строка Rails как внешний ключ

У меня есть связь между User и Course (типичные данные о зачислении). A User has_many Course и наоборот (типичный сценарий JOIN таблицы).

Я пытаюсь перенести свои предыдущие отношения has_and_belongs_to_many между этими двумя моделями на отношения has_many :through. Мои файлы сейчас выглядят так:

class User < ActiveRecord::Base

    has_and_belongs_to_many :courses
end

а также

class Course < ActiveRecord::Base

    has_and_belongs_to_many :users
end

а имя таблицы, объединяющей две модели, - courses_users.

Теперь мне нужно перенести это отношение на ассоциацию has_many :through, а также сделать тип столбца user_id a string, поскольку я хочу использовать атрибут g_number (строка) User в качестве внешнего ключа. Примечание. Меня не волнует разница в производительности между int и varchar/string.

Краткая и простая проблема заключается в том, что мне нужно users.g_number ссылаться на enrollments.user_id как на внешний ключ, и оба являются строками.

Моя попытка миграции и доработки модели такова:

class User < ActiveRecord::Base

    has_many :enrollment
    has_many :courses, :through => :enrollment
end

а также

class Course < ActiveRecord::Base

    has_many :enrollment
    has_many :users, :through => :enrollment
end

наконец

class Enrollment < ActiveRecord::Base

    belongs_to :course
    belongs_to :user
end

затем миграция

class ChangeUserIdJoin < ActiveRecord::Migration
    def self.up
        rename_table :courses_users, :enrollments
    end

    def self.down
        rename_table :enrollments, :courses_users
    end
end

Здесь все работает нормально. Я могу выполнять такие запросы, как User.courses и Course.users. Но теперь я хочу изменить тип столбца user_id в таблице соединений на string, чтобы я мог сохранить g_number (строковый атрибут на User) и присоединиться к этому вместо последовательного столбца id в User.

Когда я пытаюсь изменить тип столбца user_id на string в миграции:

class ChangeUserIdJoin < ActiveRecord::Migration
    def self.up
        change_column :courses_users, :user_id, :string
        rename_table :courses_users, :enrollments
    end

    def self.down
        rename_table :enrollments, :courses_users
        change_column :courses_users, :user_id, :integer
    end
end

запросы Course.users и User.courses начинают терпеть неудачу (ниже из консоли Rails). User.courses возвращает пустой массив (тогда как раньше было несколько объектов Course), а Course.users выдает исключение из-за несовпадения типов столбцов (что, очевидно, имеет смысл):

u = User.take
  User Load (0.9ms)  SELECT  "users".* FROM "users"  LIMIT 1
 => #<User id: 1, username: "director", g_number: "g00000000", password_digest: "$2a$10$dvcOd3rHfbcR1Rn/D6VhsOokj4XiIkQbHxXLYjy5s4f...", created_at: "2016-01-06 01:36:00", updated_at: "2016-01-06 01:36:00", first_name: "Director", last_name: "", role: 0, registered: true> 
2.1.5 :002 > u.courses
  Course Load (0.9ms)  SELECT "courses".* FROM "courses" INNER JOIN "enrollments  ON "courses"."id" = "enrollments"."course_id" WHERE "enrollments"."user_id" = $1  [["user_id", 1]]
 => #<ActiveRecord::Associations::CollectionProxy []> 
2.1.5 :003 > c = Course.take
  Course Load (0.7ms)  SELECT  "courses".* FROM "courses"  LIMIT 1
 => #<Course id: 12754, year: 2015, semester: 0, department: 7, course: 101, section: 1, name: "SPA 101 01 - Elementary Spanish I"> 
2.1.5 :004 > c.users
PG::UndefinedFunction: ERROR:  operator does not exist: integer = character varying
LINE 1: ... "users" INNER JOIN "enrollments" ON "users"."id" = "enrollm...

Мне нужно присоединиться к enrollments.user_id = users.g_number. Что мне нужно сделать, чтобы изменить столбец user_id на тип string в модели / таблице Enrollment и по-прежнему иметь возможность выполнять запросы Active Record, такие как User.courses и Course.users?


person Chris Cirefice    schedule 06.01.2016    source источник


Ответы (1)


Попробуйте указать внешний и первичный ключи в модели регистрации, например:

belongs_to :user foreign_key: :user_id, primary_key: :g_number
person rohan    schedule 06.01.2016
comment
Вы спасатель. Я работал над этим около 6 часов, пытаясь выяснить правильные отношения между ними! Чтобы обратный запрос работал (Course.users), мне также нужно было добавить has_many :enrollment, foreign_key: :user_id, primary_key: :g_number в модель User. - person Chris Cirefice; 06.01.2016