如何让graphql-ruby序列化ActiveRecord查询中的非数据库派生列
Got it, let's break this down into two key parts: making the temporary new_column accessible as a model instance attribute, and getting it to serialize properly with graphql-ruby. I'll cover solutions for both Active Record (the most common Rails ORM) and Sequel since your code syntax fits either.
For Active Record
1. Accessing the Temporary Column as an Attribute
Active Record doesn't automatically generate getters for non-database columns in your select, but the value is still stored in the instance's attributes hash. You have two clean ways to expose it as a regular attribute without modifying your model permanently:
Option A: Dynamic Instance Methods
Add a getter method directly to each instance in your result set (no model changes needed):
# Build your query as usual result = Model.select("#{Model.table_name}.*", "47 AS new_column") .where(...) .order(...) .limit(...) # Add the getter method to each instance result.each do |instance| instance.define_singleton_method(:new_column) do self.attributes['new_column'] end end # Now you can access it like a regular attribute result.first.new_column # => 47
Option B: Temporary Model Attribute Definition
Temporarily add the attribute to your model, run the query, then clean it up. This works if you don't want to loop through instances:
# Temporarily register the attribute with Active Record Model.attribute :new_column, :integer, default: nil # Run your query result = Model.select("#{Model.table_name}.*", "47 AS new_column") .where(...) .order(...) .limit(...) .to_a # Clean up: remove the temporary attribute Model.remove_attribute(:new_column) # Access the attribute normally result.first.new_column # => 47
2. GraphQL-Ruby Serialization
To serialize this temporary column in your GraphQL schema, just define a field that pulls the value directly from the instance. It works with either option above:
# In your GraphQL type definition class ModelType < Types::BaseObject field :id, ID, null: false # ... other existing fields field :new_column, Integer, null: true def new_column # Use this if you went with Option A or B object.new_column # Or if you skipped adding the getter, pull directly from attributes: # object.attributes['new_column'] end end
For Sequel ORM
Sequel makes this even simpler, since it handles temporary columns more natively:
1. Accessing the Temporary Column as an Attribute
By default, Sequel stores all selected columns in the instance's values hash. To access it like a regular attribute without adding a permanent computed property, use the dynamic_attributes plugin (you can load it temporarily or permanently, but temporary is better if you don't need it elsewhere):
# Load the plugin temporarily Model.plugin :dynamic_attributes # Build your query result = Model.select(Model.table_name + '.*') .select(47.as(:new_column)) .where(...) .order(...) .limit(...) .to_a # Optional: remove the plugin if you don't need it anymore Model.unplugin :dynamic_attributes # Access the attribute directly result.first.new_column # => 47
2. GraphQL-Ruby Serialization
Just like with Active Record, define a field that retrieves the value from the instance:
class ModelType < Types::BaseObject field :id, ID, null: false # ... other fields field :new_column, Integer, null: true def new_column # Use the dynamic attribute getter object.new_column # Or pull directly from values: # object[:new_column] end end
Both approaches let you keep your model clean of permanent computed properties while making the temporary column work exactly like a native attribute, including with GraphQL serialization.
内容的提问来源于stack exchange,提问作者Richard Haven

