Environment
forest_admin_agent 1.35.3
forest_admin_datasource_active_record 1.35.3
forest_admin_rails 1.35.3
- Ruby 4.0.2 / Rails 8.1, PostgreSQL
support_polymorphic_relations: true enabled on the datasource
Summary
When a collection with a polymorphic belongs_to contains at least one record, opening its list view fails on the frontend with:
UnknownClientRecordAgentError: Expected id to be a string or number, received null
at O.fetchRecords (feature.ts:440)
{requestType: 'list'}
The list becomes completely unusable ("An error occurred. Please try again…"). The backend returns 200 OK — the payload itself is malformed.
Reproduction
Models:
class Address < ApplicationRecord
belongs_to :addressable, polymorphic: true
end
class Contact < ApplicationRecord
has_one :address, as: :addressable
end
Create one Address linked to a Contact, then open the Address list in the UI.
Root cause
For a PolymorphicManyToOne, QueryStringParser#build_projection_fields builds a wildcard sub-projection:
# forest_admin_agent/utils/query_string_parser.rb:72
when 'PolymorphicManyToOne'
"#{field_name}:*"
But a polymorphic belongs_to can't be SQL-joined, so the AR datasource skips it while building the SELECT:
# forest_admin_datasource_active_record/utils/query.rb:208
next if relation_schema.type == 'PolymorphicManyToOne'
The record therefore comes back with a phantom related object that has no primary key:
{ "addressable_id" => 1, "addressable_type" => "Contact", "addressable" => { "*" => nil } }
The serializer then reads the pk from that phantom record, producing an empty id in two places:
- Relationship linkage:
"addressable": { "data": { "type": "Contact", "id": "" } }
- Compound
included document (note the empty self-link):
"included": [{ "type": "Contact", "id": "", "links": { "self": "/forest/Contact/" } }]
EmberData rejects the empty id → the whole list fails to render.
Expected behavior
The linkage id (and the included resource id, if any) should be built from the foreign key already present on the owner record (addressable_id + addressable_type), which does not require loading the related record.
Notes
- Only reproduces when the collection has ≥ 1 record (empty polymorphic collections render fine, which masks the bug).
Workaround (in our app, not a gem fix)
Since the related record's primary key can't come from a SQL join for a polymorphic
belongs_to, we rebuild it from the foreign key already present on the owner record.
No extra query is issued (the FK is already loaded in memory):
ForestAdminAgent::Serializer::ForestSerializer.prepend(Module.new do
def has_one_relationship(attribute_name, attr_data)
object = super
return object unless object.is_a?(Hash)
collection = ForestAdminAgent::Facades::Container.datasource.get_collection(
@options[:class_name].gsub("::", "__")
)
field = collection.schema[:fields][attribute_name.to_s]
return object unless field&.type == "PolymorphicManyToOne"
foreign_key = @object[field.foreign_key]
foreign_type = @object[field.foreign_key_type_field]
return nil if foreign_key.nil? || foreign_type.nil?
primary_key = (field.foreign_key_targets || {})[foreign_type] || "id"
object.merge(primary_key => foreign_key)
end
end)
Verified: fixes both the relationship linkage and the included document, no N+1
(0 extra SQL queries measured serializing 50 polymorphic rows), and unlinked
records (foreign_key nil) correctly omit the data key instead of crashing.
The proper fix probably belongs in ForestSerializer#has_one_relationship or in the
datasource's projection handling for PolymorphicManyToOne (query.rb:208), so the
sub-projection isn't silently skipped.
Environment
forest_admin_agent1.35.3forest_admin_datasource_active_record1.35.3forest_admin_rails1.35.3support_polymorphic_relations: trueenabled on the datasourceSummary
When a collection with a polymorphic
belongs_tocontains at least one record, opening its list view fails on the frontend with:The list becomes completely unusable ("An error occurred. Please try again…"). The backend returns 200 OK — the payload itself is malformed.
Reproduction
Models:
Create one
Addresslinked to aContact, then open the Address list in the UI.Root cause
For a
PolymorphicManyToOne,QueryStringParser#build_projection_fieldsbuilds a wildcard sub-projection:But a polymorphic
belongs_tocan't be SQL-joined, so the AR datasource skips it while building the SELECT:The record therefore comes back with a phantom related object that has no primary key:
The serializer then reads the pk from that phantom record, producing an empty id in two places:
includeddocument (note the empty self-link):EmberData rejects the empty id → the whole list fails to render.
Expected behavior
The linkage
id(and theincludedresource id, if any) should be built from the foreign key already present on the owner record (addressable_id+addressable_type), which does not require loading the related record.Notes
Workaround (in our app, not a gem fix)
Since the related record's primary key can't come from a SQL join for a polymorphic
belongs_to, we rebuild it from the foreign key already present on the owner record.No extra query is issued (the FK is already loaded in memory):
Verified: fixes both the relationship linkage and the
includeddocument, no N+1(0 extra SQL queries measured serializing 50 polymorphic rows), and unlinked
records (
foreign_keynil) correctly omit thedatakey instead of crashing.The proper fix probably belongs in
ForestSerializer#has_one_relationshipor in thedatasource's projection handling for
PolymorphicManyToOne(query.rb:208), so thesub-projection isn't silently skipped.