Rails - правильные ассоциации для уже существующих моделей - PullRequest
0 голосов
/ 20 ноября 2018

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

Существует 1 MainTable, который имеет 1 ChildTable.

Проблема может заключаться в том, что «внешние ключи»именуются по-разному в каждой таблице

  class MainTable < ActiveRecord::Base
    has_many :child_tables, :class_name => 'ChildTable', :foreign_key => "child_column_name"
    accepts_nested_attributes_for :child_tables
    self.primary_key = "main_table_column_name"
    self.table_name = 'main_table'
  end


  class ChildTable < ActiveRecord::Base
        belongs_to :main_table, :class_name => 'MainTable', foreign_key: "child_column_name", :primary_key => "main_table_column_name"
        self.table_name = 'child_table' 
        self.primary_key = "child_table_column_name"

      end

Учитывая, что в каждой основной таблице будет только 1 запись на дочернюю таблицу, но в основной таблице может быть много дочерних записей - эти ассоциации выглядят правильно?

Я хочу иметь возможность сделать что-то вроде:

m = MainTable.new
m.some_value = "123"
m.mntbl_key ="999"
c = ChildTable.new
c.something = "Foo"
m.child_tables << c

m.save!

РЕДАКТИРОВАТЬ: я больше не получаю сообщение об ошибке, так как я обновил код выше.Теперь это работает, но я должен назначить идентификаторы на обеих таблицах.Если я назначу идентификатор для главного объекта, он не автоматически даст дочернему объекту этот идентификатор в столбце fk, например:

m = MasterTable.new
m.master_table_column_name = 99
c = ChildTable.new
m.child_tables << c
m.save!

-- This will Insert ok into Master but will INSERT a blank row into the child

Однако:

m = MasterTable.new
m.master_table_column_name = 99
c = ChildTable.new
c.child_column_name = 99
m.child_tables << c
m.save!

--This works and inserts correctly into both tables

--------------------------------- СТАРЫЙ: Но я получаю сообщение об ошибке:

ActiveModel::MissingAttributeError: can't write unknown attribute `ctbl_key'

Я что-то упустил?

Ответы [ 2 ]

0 голосов
/ 20 ноября 2018

Ассоциация «один ко многим» использует только один столбец внешнего ключа, а не два.

Обычно в рельсах его настройки таковы:

class Parent 
  has_many :children
end

class Child
  belongs_to :parent
end

Фактическая ассоциация сохраняется в столбце children.parent_id.

Допустим, у нас есть нестандартный внешний ключ:

class Parent 
  has_many :children, foreign_key: 'padre_id'
end

class Child
  belongs_to :parent, foreign_key: 'padre_id'
end

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

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

class Parent 
  self.primary_key = :custom_pk
  has_many :children, foreign_key: 'padre_id'
end

class Child
  self.table_name = 'bar'
  # this will correctly reference parents.foo
  belongs_to :parent, foreign_key: 'padre_id'
end

Существует также универсальная проблема, связанная с сохранением записи.прежде чем вы сможете добавить в него потомков:

irb(main):024:0> m = MainTable.new
=> #<MainTable custom_pk: nil, created_at: nil, updated_at: nil>
irb(main):025:0> m.child_tables.new
=> #<ChildTable id: nil, padre_id: nil, created_at: nil, updated_at: nil>
irb(main):026:0> m.save!
   (0.3ms)  BEGIN
   (0.4ms)  ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Child tables is invalid
    from (irb):26

Начиная с Rails 5 belongs_to ассоциации не являются необязательными по умолчанию.Таким образом, экземпляр child_tables недопустим, поскольку padre_id равен nil.

Сначала необходимо сохранить родительскую запись:

irb(main):033:0> m = MainTable.create!
   (0.5ms)  BEGIN
  MainTable Create (0.7ms)  INSERT INTO "main_table" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "custom_pk"  [["created_at", "2018-11-20 17:31:09.545476"], ["updated_at", "2018-11-20 17:31:09.545476"]]
   (0.8ms)  COMMIT
=> #<MainTable custom_pk: 5, created_at: "2018-11-20 17:31:09", updated_at: "2018-11-20 17:31:09">
irb(main):034:0> m.child_tables.new
=> #<ChildTable id: nil, padre_id: 5, created_at: nil, updated_at: nil>
irb(main):035:0> m.save
   (0.3ms)  BEGIN
  MainTable Load (0.6ms)  SELECT  "main_table".* FROM "main_table" WHERE "main_table"."custom_pk" = $1 LIMIT $2  [["custom_pk", 5], ["LIMIT", 1]]
  ChildTable Create (0.9ms)  INSERT INTO "child_table" ("padre_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["padre_id", 5], ["created_at", "2018-11-20 17:31:21.737989"], ["updated_at", "2018-11-20 17:31:21.737989"]]
   (0.6ms)  COMMIT
=> true
irb(main):036:0> 

Или выполнить сборку из ассоциации own_to надругая сторона:

irb(main):027:0> c = ChildTable.new
=> #<ChildTable id: nil, padre_id: nil, created_at: nil, updated_at: nil>
irb(main):028:0> c.build_main_table
=> #<MainTable custom_pk: nil, created_at: nil, updated_at: nil>
irb(main):029:0> c.save!
   (0.3ms)  BEGIN
  MainTable Create (0.8ms)  INSERT INTO "main_table" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "custom_pk"  [["created_at", "2018-11-20 17:24:29.344332"], ["updated_at", "2018-11-20 17:24:29.344332"]]
  ChildTable Create (1.1ms)  INSERT INTO "child_table" ("padre_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["padre_id", 3], ["created_at", "2018-11-20 17:24:29.346764"], ["updated_at", "2018-11-20 17:24:29.346764"]]
   (0.7ms)  COMMIT
=> true
0 голосов
/ 20 ноября 2018

Не уверен, что это проблема, с которой вы столкнулись, но в данный момент вы инициализируете новые MainTables и ChildTables, но не сохраняете их.

Это означает, что им не будет назначен идентификатор, что, в свою очередь, означает, что при попытке назначить дочернюю таблицу основной таблице, у нее нет идентификатора основной таблицы (или самой себя) длясоздайте запись соединения с помощью.

Попробуйте сохранить записи перед добавлением в дочерние записи

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...