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

配置成功:has_many后,反向:belongs_to关联返回nil问题排查

ForemanCnames插件关联问题排查与解决

问题背景

正在为Foreman v3.5.1开发ForemanCnames插件,用于为网络接口模型(Nic::Base)添加CNAME记录支持。期望的关联关系中,红色箭头表示当前失效的连接:
ForemanCname模型关联关系

问题描述

可从已知NIC获取对应CNAME,但无法从给定CNAME获取对应的NIC或Domain:

irb(main):001:0> n = Nic::Base.find(7958)
=> <Nic::Managed id: 7958, name: "satellite-test-01.scc.kit.edu", ...>

irb(main):002:0> a = n.host_aliases.first
=> <ForemanCnames::HostAlias id: 5, name: "newer-alias", nics_id: 7958, domains_id: 1>

irb(main):004:0> a.nic
=> nil

irb(main):006:0> a.domain
=> nil

irb(main):007:0> n.domain
=> <Domain id: 1, name: "scc.kit.edu", ...>

Rails模型定义

HostAlias模型代码

module ForemanCnames
  class HostAlias < ActiveRecord::Base
    belongs_to :domain
    belongs_to :nic, :class_name => '::Nic::Base', :inverse_of => :host_aliases

    validates :nics_id, :presence => true
    validates :name, :presence => true, :uniqueness => {:scope => :domains_id}

    def to_s
      name
    end

    def cname
      Nic::Interface.find(nics_id).fqdn
    end
  end
end

Nic::Base关联扩展(Concern)

module ForemanCnames::Concerns::NicExtensions
  extend ActiveSupport::Concern

  included do
    has_many :host_aliases, :foreign_key => :nics_id, :class_name => 'ForemanCnames::HostAlias', :inverse_of => :nic
    accepts_nested_attributes_for :host_aliases, allow_destroy: true
  end
end

数据库迁移文件

class CreateHostAliases < ActiveRecord::Migration[4.2]
  def change
    create_table :foreman_cnames_host_aliases do |t|
      # 默认添加主键id列
      t.string :name, :limit => 255
      t.references :nics, null: false, foreign_key: true
      t.references :domains, null: false, foreign_key: true

      t.timestamps
    end
  end
end

未给Domain添加关联,因无需通过Domain查找CNAME。

问题分析

  1. HostAlias#cname功能正常:
irb(main):001:0> ForemanCnames::HostAlias.first.cname
=> "satellite-test-01.scc.kit.edu"
  1. 关联方法返回nil:理论上HostAlias#nic应通过Nic::Base.find(nics_id)实现,但Rails自动生成的关联方法返回nil,且无法查看对应SQL:
irb(main):001:0> ForemanCnames::HostAlias.first.method(:nic).source_location
=> ["/usr/share/gems/gems/activerecord-6.1.7/lib/active_record/associations/builder/association.rb", 102]

irb(main):002:0> ForemanCnames::HostAlias.first.nic.to_sql
Traceback (most recent call last):
        2: from lib/tasks/console.rake:5:in `block in <top (required)>'
        1: from (irb):2
NoMethodError (undefined method `to_sql' for nil:NilClass)

irb(main):003:0> puts Nic::Base.find(7958).host_aliases.to_sql
SELECT "foreman_cnames_host_aliases".* FROM "foreman_cnames_host_aliases" WHERE "foreman_cnames_host_aliases"."nics_id" = 7958
  1. :inverse_of配置无效:添加前后问题未改善。
  2. 关联反射检查:发现:nic和:domain的外键被识别为nic_id和domain_id,但数据库表实际字段为nics_id和domains_id,这是问题根源:
irb(main):001:0> ForemanCnames::HostAlias.reflect_on_association(:nic).association_primary_key
=> "id"

irb(main):002:0> ForemanCnames::HostAlias.reflect_on_association(:nic).foreign_key
=> "nic_id"

irb(main):003:0> ForemanCnames::HostAlias.reflect_on_association(:domain).association_primary_key
=> "id"

irb(main):004:0> ForemanCnames::HostAlias.reflect_on_association(:domain).foreign_key
=> "domain_id"

irb(main):005:0> ForemanCnames::HostAlias
=> ForemanCnames::HostAlias(id: integer, name: string, nics_id: integer, domains_id: integer)

解决方案

在HostAlias模型的belongs_to关联中显式指定外键,匹配数据库表中的字段名:

module ForemanCnames
  class HostAlias < ActiveRecord::Base
    # 显式指定外键为domains_id
    belongs_to :domain, foreign_key: :domains_id
    # 显式指定外键为nics_id
    belongs_to :nic, class_name: '::Nic::Base', inverse_of: :host_aliases, foreign_key: :nics_id

    validates :nics_id, :presence => true
    validates :name, :presence => true, :uniqueness => {:scope => :domains_id}

    def to_s
      name
    end

    def cname
      Nic::Interface.find(nics_id).fqdn
    end
  end
end

修改后,Rails将使用正确的外键字段查询关联记录,a.nic和a.domain即可返回对应对象。

补充说明

后续若需通过Host查找CNAME,可在Host::Base中添加关联:has_many :host_aliases, through: :interfaces,但此非当前问题的解决方案。


内容的提问来源于stack exchange,提问作者Xavier Mol

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 09:58:14