プラグインでHasCustomFieldsを使用する

プラグイン内にモデルがあります。そのモデルは Pfaffmanager::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 を指すように更新しましたか?

「いいね!」 2

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

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 はもう一方の形式を求めているようです。

インデックス名については気にしないのでしょうか?

「いいね!」 1

たぶん関係ないと思います。

おそらく、2 番目のアプローチ(カラム名として pfaffman_server_id を使用するもの)を目指すのがよいでしょう。

このメソッドをオーバーライドすることで解決するかもしれません:

現在の値をコンソールで確認するには、以下のようにします:

Server.new.custom_fields_fk

これが server_id である場合、Server モデルでそのメソッドをオーバーライドすることをお勧めします。

class Server < ...
  def custom_fields_fk
    "pfaffman_server_id"
  end
end
「いいね!」 1

それとも、この部分でしょうか :thinking:

「いいね!」 1

ありがとうございます!では、pfaffmanager_serfver_id を使用する場合、Server.new.custom_fields_fkpfaffmanager_server_id になります。そのため、おそらく server_id を使用し、その後、上記で提案したようにオーバーライドすべきでしょう。

refresh_custom_fields_from_db が間違った名前を使用しようとしているようです。_custom_fields.order... が何をしているのか、まだよく理解できていません。

server_id を使用した場合にどうなるか、そしてその後 custom_fields_fk をオーバーライドできるかどうかを確認してみます。

「いいね!」 1

やったね!本当にありがとう。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? で頭を叩きながら進められそうです。その後、新しいカスタムフィールドに何かを埋め込むようなルートも追加できます。

本当に感謝しています。おそらく1日分を節約してくれました。ビールを一杯おごりますよ!:beer:

「いいね!」 1

素晴らしいです!:tada: より汎用的にする方法をご提案いただけるようであれば、ぜひプルリクエストをお待ちしています。

「いいね!」 1

何か思いついたらお知らせするか、PR を提出します。ただし、カスタムフィールドを使用したいプラグインは、かなりニッチな領域にあると考えています。

「いいね!」 1

OM_fk_G.

custom_fields_fk は非常に愚かに見えたため、削除するつもりで、後で削除しても安全だとわかるように愚かな名前を付けたのだと推測しました。後で削除しました。すると、私の仕様が失敗しました。

コード内には、このトピックへのリンクを含むコメントさえありました。

「いいね!」 1