플러그인에서 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

server_id 대신 pfaffmanager_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이면 저장이 실패합니다.

concerns/has_custom_fields.rb의 save_custom_fields를 보고 있지만, 이를 오버라이드할 방법이 있는지 정확히 파악하지 못하고 있습니다.

pfaffmanager_server 대신 server를 사용하도록 전체 구조를 재구성하는 것 외에 할 수 있는 방법이 있을까요?

add_index 호출도 pfaffmanager_server_id를 가리키도록 업데이트했나요?

대단히 감사합니다, David!

네. 제 문제가 바로 그것일 수 있겠네요. 아마 그 에러 메시지를 포함했어야 했는데요. 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

save를 시도했을 때 저장을 시도하기 전까지는 nil을 반환할 수 있었지만, 커스텀 필드를 저장하려고 했을 때 실패했습니다. s.custom_fields를 시도하면 다음과 같은 일이 발생합니다. 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_serfver_id로 사용하려면 pfaffmanager_server_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 같은 다른 것들을 깨뜨리지 않는다면, 이제 https://meta.discourse.org/t/best-way-to-enforce-permissions-controller-or-constraint/176113에서 다시 머리를 쥐어박을 준비가 된 것 같아. 그리고 나서 내 새로운 커스텀 필드에 . . . 무언가를 채우는 등의 작업을 수행할 라우트를 추가할 수 있을 거야.

얼마나 고마운지 모를 정도야. 아마 하루치 시간을 아껴준 것 같아. :beer: 한 잔 사줄게!

좋아요! :tada: 더 범용적으로 만들 수 있는 방법을 생각할 수 있다면, PR을 환영합니다.

무언가 떠오르면 알려드리거나 PR을 제출하겠습니다. 하지만 커스텀 필드를 사용하려는 플러그인은 꽤나 비현실적이라고 생각합니다.

OM_fk_G.

custom_fields_fk라는 이름이 너무 멍청해서, 나중에 지워도 안전하다는 걸 알기 위해 일부러 그렇게 지은 것인 줄 알았어요. 그리고 정말 나중에, 저는 그걸 삭제했습니다. 그랬더니 스펙이 실패했습니다.

코드에는 이 주제에 대한 링크가 있는 주석까지 있었습니다.