1

我有一个模型Download,有一张桌子downloadsdownloads有一个名为 的字段ip_address,它将 IP 地址存储为整数。我想建立一个IpAddress模型,但没有 ip_addresses 表,所以我可以做类似的事情

Download.find(1).ip_address.to_s # '127.0.0.1'
Download.find(1).ip_address.to_i # 2130706433
Download.find(1).ip_address.downloads # SELECT * FROM downloads WHERE ip_address='2130706433'
IpAddress.find(2130706433).downloads # SELECT * FROM downloads WHERE ip_address='2130706433'

我希望它表现得像:

class Download < ActiveRecord::Base
  belongs_to :ip_address, :foreign_key => :ip_address
end

class IpAddress < ActiveRecord::Base
  set_primary_key :ip_address
  has_many :downloads, :foreign_key => :ip_address
end

但没有无用的 IP 地址表。

这可能吗?

编辑

我发现 ruby​​ 已经有一个IPAddr 类

所以我这样做了:

require 'ipaddr'

class Download < ActiveRecord::Base
  attr_accessible :ip, ...

  def ip
    @ip ||= IPAddr.new(read_attribute(:ip), Socket::AF_INET)
  end

  def ip=(addr)
    @ip = IPAddr.new(addr, Socket::AF_INET)
    write_attribute(:ip, @ip.to_i)
  end

  def self.for_ip(addr)
    where(:ip => IPAddr.new(addr, Socket::AF_INET).to_i)
  end

end

然后我可以做很多很酷的事情

Download.new(:ip => '127.0.0.1').save
Download.for_ip('127.0.0.1').first.ip.to_i # 2130706433
4

3 回答 3

3

belongs_to实际上是为了指定两个表中的对象之间的关联。但你是对的,除非你需要存储其他相关数据,否则将 IP 地址存储在表中是相当无用的。

但是,您可以使用范围来完成您想要的。你可以在你的下载模型中有这样的东西:

class Download < ActiveRecord::Base
  scope :for_ip, lambda { |x| where(:ip_address => x) }
end

然后你会打电话

Download.for_ip(2130706433)

获取该 IP 的下载列表。

您也可以添加一个类方法:

class Download < ActiveRecord::Base
  def self.for_ip(x)
    where(:ip_address => x)  
  end  
end

如果您想从字符串转换为数字 IP 地址,这可能会很方便。

而且,如果你想要一个 IPAddress 类,你可以添加这样的方法:

class IPAddress
  def initialize(ip)
    #presumably do some stuff here
    @ip = ip
  end

  def downloads
    Download.for_ip(@ip)
  end
end
于 2012-08-10T14:50:39.447 回答
2
IpAddress.find(2130706433).downloads # SELECT * FROM downloads WHERE ip_address='2130706433'

这完全是一个语义问题,但是如果您没有 IpAddress 表,这可能会改变(即,如果没有 IpAddress 表,我们如何在数据库中找到 IpAddress 对象 2130706433 - 除非您将 IpAddress 设为容器而不是特定的单个 ipaddress , 否则做一些事情,比如用像IpAddress(2130706433).downloads) 这样的构造函数来实例化新的。

但是,否则,我认为没有 IpAddress 表没有任何问题。为什么需要它belongs_to,而不仅仅是另一列?

如果您希望以类似方式访问它们,您可以保留模型/对象:

class Download < ActiveRecord::Base
  ##Whatever Download-model-specific code you have...
  def ip_address
    #If nil, initialize new object. Return object.
    @ip_address ||= IpAddress(ip_address_val)
  end
end

class IpAddress
  def initialize(address)
    @value = address
  end
  def downloads
    Download.where(:ip_address_val => self.value)
  end
end

编辑: 你可以覆盖访问器,就像你问的那样。您只需要在代码中小心谨慎,以特别注意您所要求的内容。请参阅此文档:http ://ar.rubyonrails.org/classes/ActiveRecord/Base.html 在“覆盖默认访问器”部分下

基本上,如果您确实覆盖了该值,并且如果您希望访问 DB 值,则使用read_attribute(attr_name),因此代码可能如下所示:

class Download < ActiveRecord::Base
  ##Whatever Download-model-specific code you have...
  def ip_address
    #If nil, initialize new object. Return object.
    @ip_address ||= IpAddress(read_attribute(:ip_address))
  end
end

class IpAddress
  def initialize(address)
    @value = address
  end
  def downloads
    Download.where(:ip_address => self.value)
  end
end

虽然如果你不小心的话,你的代码可能会有点混乱。

于 2012-08-10T14:52:28.750 回答
0

将 set_table_name "downloads" 添加到您的 IpAddress 并删除 2 之间的关系,因为它已经具有列名 ip_address。

这将通过以下方式为您提供查询

Download.find(1).ip_address.to_s # '127.0.0.1'
Download.find(1).ip_address.to_i # 2130706433
IpAddress.find(Download.find(1).ip_address) # SELECT * FROM downloads WHERE ip_address='2130706433'
IpAddress.find(2130706433) # SELECT * FROM downloads WHERE ip_address='2130706433'
于 2012-08-10T14:49:48.457 回答