PAYATHON 2026

Payment 关联的是 payee 还是 User?

支付阿杰

结论

Payment 实际关联的是 User 模型,payee 是这条关联在 Payment 中的名称。

具体来说:

  • User:实际关联的模型类。
  • payee:Payment 用来访问收款用户的关联名。
  • received_payments:User 用来访问收款记录的反向关联名。
  • payee_id:payments 表中的外键,指向 users.id。

换句话说,一个 Payment 属于一个作为 payee 的 User,一个 User 可以拥有多条 received_payments。

为什么关联名是 payee

Rails 通常会根据 belongs_to 后面的名称推断模型:

belongs_to :user

这表示关联的模型是 User,外键通常是 user_id。

这里使用 payee,是因为它比笼统的 user 更贴近业务含义。由于关联名和模型名不同,需要用 class_name 指定实际模型:

belongs_to :payee, class_name: "User"

这样调用:

payment.payee

返回的是一个 User 对象。Rails 默认通过 payee_id 查询这个用户,效果类似于:

User.find(payment.payee_id)

payee 不是另一个模型,也不需要定义 Payee 类。它只是一个指向 User 的关联名称。

反向关联如何工作

User 中的定义如下:

class User < ApplicationRecord
  has_many :received_payments,
           foreign_key: "payee_id",
           class_name: "Payment"
end

其中:

  • received_payments 是关联名称。
  • class_name: "Payment" 表示实际关联的模型是 Payment。
  • foreign_key: "payee_id" 表示外键位于 payments 表中。

因此,调用:

user.received_payments

会查找所有符合以下条件的 Payment:

Payment.where(payee_id: user.id)

对应的数据库关系是:

payments.payee_id → users.id

推荐写法

可以补充 inverse_of,让 Rails 明确识别这组双向关联:

class Payment < ApplicationRecord
  belongs_to :payee,
             class_name: "User",
             inverse_of: :received_payments
end
class User < ApplicationRecord
  has_many :received_payments,
           class_name: "Payment",
           foreign_key: :payee_id,
           inverse_of: :payee
end

创建和访问数据时可以这样写:

user = User.find(1)

payment = Payment.create!(
  payee: user
)

payment.payee
# => user 对应的 User 实例

user.received_payments
# => payee_id 等于 user.id 的 Payment 集合

也可以从用户一侧创建:

payment = user.received_payments.create!

Rails 会自动将 payment.payee_id 设置为 user.id。

数据库需要具备的字段

payments 表必须包含 payee_id 字段。例如,迁移可以写成:

class AddPayeeToPayments < ActiveRecord::Migration[7.0]
  def change
    add_reference :payments,
                  :payee,
                  foreign_key: { to_table: :users },
                  index: true
  end
end

这里需要注意:

foreign_key: { to_table: :users }

引用列虽然叫 payee_id,但它指向的不是 payees 表,而是 users 表。

迁移版本号应按项目实际使用的 Rails 版本调整。

常见误区

payee 不是数据库表名

下面的关联不要求存在 payees 表:

belongs_to :payee, class_name: "User"

class_name: "User" 已经指定 Rails 查询 users 表。

received_payments 也不是模型名

下面的代码返回 Payment 集合:

user.received_payments

Rails 不会寻找 ReceivedPayment 模型,因为关联中已经指定:

class_name: "Payment"

两侧名称不需要相同

下面两个名称描述的是同一组关系的不同方向:

payment.payee
user.received_payments

前者表示“这笔付款的收款人”,后者表示“这个用户收到的付款”。使用不同的关联名,可以让各自的业务含义更清楚。

备注:内容仅供参考。