You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails GraphQL中为Type类添加非模型自定义字段的实现问题

问题描述

我定义的PaymentType GraphQL类型比对应的Payment ActiveRecord模型多了一个xyz字段,希望在CreatePayment突变的resolve方法中获取xyz的数据,返回包含该字段的PaymentType实例。尝试过直接给payment变量添加属性、给PaymentType编写构造函数,均出现错误。

可行解决方案

方案1:给Payment模型添加虚拟属性

通过attr_accessor给模型添加虚拟属性,让ActiveRecord实例支持读写xyz字段,GraphQL会自动识别该属性:

修改Payment模型:

class Payment < ActiveRecord::Base
  field :id, Integer, null: false
  field :amount, Integer, null: false
  field :currency, String, null: false

  # 添加虚拟属性,支持xyz的读写
  attr_accessor :xyz
end

修改CreatePayment的resolve方法,给payment赋值xyz:

module Mutations
  class CreatePayment < BaseMutation
    type Types::PaymentType
    description "Create Payment"

    argument :amount, Integer, required: true
    argument :currency, String, required: true

    def resolve(amount:, currency:)
      payment = Payment.new(amount: amount, currency: currency)
      xyz = SomeXYZ() # 你的业务逻辑获取xyz值
      payment.xyz = xyz
      payment.save
      payment
    end
  end
end

PaymentType保持原有定义即可:

module Types
  class PaymentType < Types::BaseObject
    field :id, Integer, null: false
    field :amount, Integer, null: false
    field :currency, String, null: false
    field :xyz, Integer, null: true
  end
end

方案2:在PaymentType的xyz字段中单独定义resolve逻辑

无需修改模型,直接在GraphQL字段中指定resolve逻辑,从返回的对象中读取xyz值:

修改CreatePayment的resolve方法,返回包含payment和xyz的结构体:

module Mutations
  class CreatePayment < BaseMutation
    type Types::PaymentType
    description "Create Payment"

    argument :amount, Integer, required: true
    argument :currency, String, required: true

    def resolve(amount:, currency:)
      payment = Payment.new(amount: amount, currency: currency)
      xyz = SomeXYZ()
      payment.save
      # 用OpenStruct包装payment数据和xyz
      OpenStruct.new(payment.attributes.merge(xyz: xyz))
    end
  end
end

修改PaymentType的xyz字段,添加自定义resolve:

module Types
  class PaymentType < Types::BaseObject
    field :id, Integer, null: false
    field :amount, Integer, null: false
    field :currency, String, null: false
    
    field :xyz, Integer, null: true do
      resolve ->(obj, args, ctx) { obj.xyz }
    end
  end
end

方案3:自定义结构体包装数据

如果不想修改模型或使用OpenStruct,可以自定义结构体来封装payment和xyz:

先定义结构体(可放在app/graphql/types/目录下):

PaymentWithXyz = Struct.new(:payment, :xyz) do
  # 委托基础字段到payment实例
  delegate :id, :amount, :currency, to: :payment
end

修改CreatePayment的resolve方法返回结构体实例:

module Mutations
  class CreatePayment < BaseMutation
    type Types::PaymentType
    description "Create Payment"

    argument :amount, Integer, required: true
    argument :currency, String, required: true

    def resolve(amount:, currency:)
      payment = Payment.new(amount: amount, currency: currency)
      xyz = SomeXYZ()
      payment.save
      PaymentWithXyz.new(payment, xyz)
    end
  end
end

修改PaymentType的字段resolve逻辑:

module Types
  class PaymentType < Types::BaseObject
    field :id, Integer, null: false, resolver: ->(obj, _, _) { obj.id }
    field :amount, Integer, null: false, resolver: ->(obj, _, _) { obj.amount }
    field :currency, String, null: false, resolver: ->(obj, _, _) { obj.currency }
    field :xyz, Integer, null: true, resolver: ->(obj, _, _) { obj.xyz }
  end
end

错误原因说明

  • 直接给payment加属性:ActiveRecord实例默认不允许读写未定义的属性,必须通过attr_accessor声明虚拟属性。
  • 给PaymentType写构造函数:GraphQL-Ruby的BaseObject由框架自动初始化解析,自定义构造函数会破坏默认逻辑,导致初始化失败。

内容的提问来源于stack exchange,提问作者anshul mittal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 11:17:49