Rails 7.1多态has_many through关联报错‘Features is invalid’求助
保存关联Product::Feature的Product::Group时出现"Features is invalid"错误
尝试保存关联多个Product::Feature的新Product::Group时,触发错误“Features is invalid”。调整过inverse_of、class_name、primary_key等所有关联参数组合均无效,目前仅能将ObjectFeature和Feature移出Product命名空间作为临时方案,求解决方法。
触发错误的代码
class Product::ObjectFeature < ApplicationRecord self.table_name = "object_features" belongs_to :feature, class_name: "Product::Feature", foreign_key: :feature_id, inverse_of: :object_features belongs_to :featurable, polymorphic: true end class Product::Feature < ApplicationRecord self.table_name = "features" has_many :object_features, foreign_key: :feature_id, inverse_of: :feature end class Product::Group < ApplicationRecord self.table_name = "product_groups" has_many :object_features, as: :featurable, inverse_of: :featurable has_many :features, through: :object_features, class_name: "Product::Feature", foreign_key: :feature_id end
可正常运行的代码(移出命名空间后)
class ObjectFeature < ApplicationRecord belongs_to :feature belongs_to :featurable, polymorphic: true end class Feature < ApplicationRecord has_many :object_features end class Product::Group < ApplicationRecord self.table_name = "product_groups" has_many :object_features, as: :featurable has_many :features, through: :object_features end
错误日志
Processing by Catalog::GroupsController#create as TURBO_STREAM Parameters: {"authenticity_token"=>"[FILTERED]", "product_group"=>{"arrangement_id"=>"1", "function_pair_id"=>"117", "product_cover_ids"=>[""], "collection_name"=>"", "loading"=>"", "spec_sheet_template"=>"upholstery_group", "feature_ids"=>["", "54", "19", "40"]}, "commit"=>"Create Group", "product_id"=>"974"} User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]] Product::Feature Load (0.2ms) SELECT "features".* FROM "features" WHERE "features"."id" IN ($1, $2, $3) [["id", 54], ["id", 19], ["id", 40]] app/controllers/default_controller.rb:111:in `set_create_object' Catalog::Product Load (0.2ms) SELECT "products".* FROM "products" WHERE "products"."id" = $1 LIMIT $2 [["id", 974], ["LIMIT", 1]] app/controllers/catalog/groups_controller.rb:157:in `set_product' Role Load (0.1ms) SELECT "roles".* FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 [["user_id", 1]] app/models/ability.rb:9:in `map' CACHE Product::Feature Load (0.0ms) SELECT "features".* FROM "features" WHERE "features"."id" IN ($1, $2, $3) [["id", 54], ["id", 19], ["id", 40]] app/controllers/catalog/groups_controller.rb:47:in `create' TRANSACTION (0.2ms) BEGIN app/controllers/catalog/groups_controller.rb:49:in `create' Catalog::Product Load (0.2ms) SELECT "products".* FROM "products" WHERE "products"."id" = $1 LIMIT $2 [["id", 974], ["LIMIT", 1]] app/controllers/catalog/groups_controller.rb:49:in `create' Product::Function Load (0.1ms) SELECT "function_pairs".* FROM "function_pairs" WHERE "function_pairs"."id" = $1 LIMIT $2 [["id", 117], ["LIMIT", 1]] app/controllers/catalog/groups_controller.rb:49:in `create' Product::Arrangement Load (0.1ms) SELECT "arrangements".* FROM "arrangements" WHERE "arrangements"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]] app/controllers/catalog/groups_controller.rb:49:in `create' ActiveStorage::Attachment Load (0.1ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = $1 AND "active_storage_attachments"."record_type" = $2 AND "active_storage_attachments"."name" = $3 LIMIT $4 [["record_id", 54], ["record_type", "Product::Feature"], ["name", "image"], ["LIMIT", 1]] app/controllers/catalog/groups_controller.rb:49:in `create' ActiveStorage::Attachment Load (0.1ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = $1 AND "active_storage_attachments"."record_type" = $2 AND "active_storage_attachments"."name" = $3 LIMIT $4 [["record_id", 19], ["record_type", "Product::Feature"], ["name", "image"], ["LIMIT", 1]] app/controllers/catalog/groups_controller.rb:49:in `create' ActiveStorage::Attachment Load (0.2ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = $1 AND "active_storage_attachments"."record_type" = $2 AND "active_storage_attachments"."name" = $3 LIMIT $4 [["record_id", 40], ["record_type", "Product::Feature"], ["name", "image"], ["LIMIT", 1]] app/controllers/catalog/groups_controller.rb:49:in `create' Product::Group Exists? (0.2ms) SELECT 1 AS one FROM "product_groups" WHERE "product_groups"."arrangement_id" = $1 AND "product_groups"."function_pair_id" = $2 AND "product_groups"."product_id" = $3 LIMIT $4 [["arrangement_id", 1], ["function_pair_id", 117], ["product_id", 974], ["LIMIT", 1]] app/controllers/catalog/groups_controller.rb:49:in `create' TRANSACTION (0.2ms) ROLLBACK app/controllers/catalog/groups_controller.rb:49:in `create' ["Features is invalid"]
解决方法
问题根源在于命名空间下的多态关联未正确指定关联模型的类名,导致Rails无法正确解析关联关系。修正后的代码如下:
class Product::ObjectFeature < ApplicationRecord self.table_name = "object_features" belongs_to :feature, class_name: "Product::Feature", inverse_of: :object_features belongs_to :featurable, polymorphic: true end class Product::Feature < ApplicationRecord self.table_name = "features" # 明确指定关联的中间模型为Product::ObjectFeature has_many :object_features, class_name: "Product::ObjectFeature", inverse_of: :feature end class Product::Group < ApplicationRecord self.table_name = "product_groups" # 明确指定关联的中间模型为Product::ObjectFeature has_many :object_features, class_name: "Product::ObjectFeature", as: :featurable, inverse_of: :featurable # through关联无需额外指定class_name和foreign_key,Rails会自动通过object_features关联解析 has_many :features, through: :object_features end
关键修正点
Product::Feature的object_features关联:必须指定class_name: "Product::ObjectFeature",否则Rails会默认查找根命名空间下的ObjectFeature模型。Product::Group的object_features关联:同样需要指定class_name: "Product::ObjectFeature",确保多态关联指向正确的中间模型。- 移除冗余参数:
through关联无需额外指定class_name和foreign_key,因为object_features已经正确关联到Product::Feature。
额外验证
确保object_features表的featurable_type字段存储的是完整的命名空间类名(如Product::Group),而非简化的Group。如果之前有旧数据,需要手动更新该字段的值,或在模型中通过self.featurable_type = self.class.name确保存储正确的类名。
内容的提问来源于stack exchange,提问作者Brian Lewis
相关产品推荐
相关产品推荐

