Использование HasCustomFields в плагине

У меня есть модель в плагине. Модель — Pfaffmananger::Server, таблица — pfaffmanager_servers. По определенным причинам я хочу добавить кастомные поля для этой модели (я предполагаю, что будут случаи, когда мне нужно расширить данные сервера, и это потребуется только для нескольких экземпляров; я не хочу добавлять в модель огромное количество полей, которые в основном не используются).

Вот миграция, создающая таблицу:

class CreatePfaffmanagerServerCustomField < ActiveRecord::Migration[6.0]
  def change
    create_table :pfaffmanager_server_custom_fields do |t|
      t.integer :server_id, null: false
      t.string :name, limit: 256, null: false
      t.text :value
      t.timestamps null: false
    end
    add_index :pfaffmanager_server_custom_fields, [:server_id, :name]
  end
end

Если я использую pfaffmanager_server_id вместо server_id, миграция не выполняется, так как не может найти server_id. Однако, если я задаю имя поля как pfaffmanager_server_id, то при попытке сохранить сервер после создания кастомного поля возникает ошибка:

  Pfaffmanager::ServerCustomField Load (0.4ms)  SELECT "pfaffmanager_server_custom_fields".* FROM "pfaffmanager_server_custom_fields" WHERE "pfaffmanager_server_custom_fields"."server_id" = 1
   (0.2ms)  ROLLBACK
PG::UndefinedColumn: ERROR:  column "pfaffmanager_server_id" of relation "pfaffmanager_server_custom_fields" does not exist
LINE 1: INSERT INTO pfaffmanager_server_custom_fields (pfaffmanager_...
    

Таким образом, если в миграции указано поле pfaffmanager_server_id, миграция не выполняется, а если имя поля server_id, то не удаётся сохранить данные.

Я изучаю метод save_custom_fields в файле concerns/has_custom_fields.rb, но пока не могу понять, есть ли способ переопределить его.

Есть ли что-то, что я могу сделать, кроме как полностью переписать код, используя server вместо pfaffmanager_server?

Вы также обновили вызов add_index, указав на pfaffmanager_server_id?

Огромное спасибо, Дэвид!

Да. Возможно, это и есть моя проблема. Думаю, именно об этой ошибке я должен был сообщить. Я могу заставить миграцию работать с любым из полей (хотя при использовании pfaffmanager_server_id как имени поля имя индекса становится > 63 символов, поэтому мне приходится использовать другое имя для индекса).

Попробую снова.

server_id как имя поля

class CreatePfaffmanagerServerCustomField < ActiveRecord::Migration[6.0]
  def change
    create_table :pfaffmanager_server_custom_fields do |t|
      t.integer :server_id, null: false
      t.string :name, limit: 256, null: false
      t.text :value
      t.timestamps null: false
    end
    add_index :pfaffmanager_server_custom_fields, [:server_id, :name]
  end
end

Неудача при сохранении:

 Pfaffmanager::ServerCustomField Load (3.8ms)  SELECT "pfaffmanager_server_custom_fields".* FROM "pfaffmanager_server_custom_fields" WHERE "pfaffmanager_server_custom_fields"."server_id" = 1
   (1.4ms)  ROLLBACK
PG::UndefinedColumn: ERROR:  column "pfaffmanager_server_id" of relation "pfaffmanager_server_custom_fields" does not exist
LINE 1: INSERT INTO pfaffmanager_server_custom_fields (pfaffmanager_...
                                                       ^

То есть система ищет pfaffmanager_server_id. Теперь попробуем использовать pfaffmanager_server_id.

pfaffmanager_server_id как имя поля

Вот миграция:

class CreatePfaffmanagerServerCustomField < ActiveRecord::Migration[6.0]
  def change
    create_table :pfaffmanager_server_custom_fields do |t|
      t.integer :pfaffmanager_server_id, null: false
      t.string :name, limit: 256, null: false
      t.text :value
      t.timestamps null: false
    end
    add_index :pfaffmanager_server_custom_fields,
      [:pfaffmanager_server_id, :name],
      name: 'index_pfaffmanager_server_custom_fields_on_server_id_and_name'

  end
end

Вот что происходит, когда я пытаюсь вызвать s.custom_fields (вышеуказанный код мог вернуть nil для этого, но ошибка возникала только при попытке сохранить пользовательское поле). Я вызываю register_custom_field_type здесь:

 pry(main)> s.custom_fields
   (1.1ms)  SELECT "pfaffmanager_server_custom_fields"."name", "pfaffmanager_server_custom_fields"."value" FROM "pfaffmanager_server_custom_fields" WHERE "pfaffmanager_server_custom_fields"."server_id" = 1 ORDER BY id asc
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR:  column pfaffmanager_server_custom_fields.server_id does not exist
LINE 1: ...e" FROM "pfaffmanager_server_custom_fields" WHERE "pfaffmana...
                                                             ^

from /home/pfaffman/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/rack-mini-profiler-2.3.0/lib/patches/db/pg.rb:69:in `exec_params'
Caused by PG::UndefinedColumn: ERROR:  column pfaffmanager_server_custom_fields.server_id does not exist
LINE 1: ...e" FROM "pfaffmanager_server_custom_fields" WHERE "pfaffmana...
                                                             ^

Теперь система ищет server_id.

Получается, что custom_fields ожидает одно, а save_custom_fields — другое.

Ему всё равно, как называется индекс, верно?

По-моему, нет.

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

Интересно, поможет ли переопределение этого метода:

Вы можете проверить текущее значение в консоли так:

Server.new.custom_fields_fk

Если там server_id, я предлагаю переопределить этот метод в вашей модели Server:

class Server < ...
  def custom_fields_fk
    "pfaffman_server_id"
  end
end

Или, может быть, вот этот момент :thinking:

Спасибо большое! Так что Server.new.custom_fields_fk — это pfaffmanager_server_id, если я использую pfaffmanager_serfver_id. Возможно, мне стоит использовать server_id, а затем переопределить его, как вы предложили выше.

Похоже, что refresh_custom_fields_from_db пытается использовать неверные имена. Я не до конца понимаю, что делает _custom_fields.order....

Я проверю, что произойдет, если я буду использовать server_id, и смогу ли я затем переопределить custom_fields_fk.

Ха! У вас получилось! Большое спасибо. Я переключил на server_id, а затем добавил это в модель server.rb:

    def custom_fields_fk
      @custom_fields_fk ||= "server_id"
    end

Пока это переопределяет значение только для этой модели и не ломает user_custom_field и подобные, я думаю, что готов снова начать биться головой об стену на Best way to enforce permissions--controller or constraint?. А потом смогу добавить маршруты, чтобы, например, заполнить мои новые пользовательские поля чем-то…

Не могу выразить вам благодарность. Вы, скорее всего, сэкономили мне целый день. Я вам должен :beer:!

Отлично! :tada: Если вы сможете придумать способ сделать это более универсальным, то это, безусловно, будет приветствоваться в виде PR.

Если что-то придет мне в голову, я дам знать или отправлю PR, но я полагаю, что плагин, который хочет использовать пользовательские поля, находится довольно далеко на периферии.

OM_fk_G.

custom_fields_fk казался настолько глупым, что я решил, будто это что-то, что я намеревался удалить, и назвал его так нарочно, чтобы потом понять: его можно смело удалять. Позже я это сделал и удалил. А потом мои тесты провалились.

В коде даже был комментарий со ссылкой на эту тему.