Rails自定义元模型?

And*_*rew 2 ruby-on-rails metamodel ruby-on-rails-3

我希望能够将"元"信息添加到模型中,基本上是用户定义的字段.所以,举个例子,让我们想象一下User模型:

我为名字,姓氏,年龄,性别定义字段.

我希望用户能够定义一些"元信息",基本上可以进入他们的个人资料页面并分享其他信息.因此,一个用户可能想要添加"爱好","职业"和"家乡",而另一个用户可能想要添加"爱好"和"教育".

所以,我希望能够为这种东西提供一个标准视图,所以例如在视图中我可以做一些像(在HAML中):

- for item in @meta
  %li
    %strong= item.key + ":"
    = item.value
Run Code Online (Sandbox Code Playgroud)

通过这种方式,我可以确保始终如一地显示信息,而不仅仅是向用户提供可以格式化所有不同方式的降价文本框.

我也很想能够点击meta并看到其他用户已经给出相同的东西,所以在上面的例子中,两个用户都定义了"爱好",能够说我希望看到用户是很好的有共同的爱好-甚至更好,我想看看如果用户的爱好是_ __.

因此,由于我不知道用户希望提前定义哪些字段,因此提供这种功能的选项有哪些?

是否有一个gem来处理这样的模型上的自定义元信息,或者至少有类似的东西?有没有人遇到过这种问题?如果是这样,你是如何解决的?

谢谢!

Har*_*tty 7

动态字段实现取决于以下因素:

  1. 能够动态添加属性
  2. 能够支持新的数据类型
  3. 无需额外查询即可检索动态属性
  4. 能够访问常规属性等动态属性
  5. 能够根据动态属性查询对象.(例如:找到有滑雪爱好的用户)

通常,解决方案不能满足所有要求.迈克的解决方案优雅地解决了1和5问题.如果1和5对您很重要,您应该使用他的解决方案.

这是一个解决1,2,3,4和5的长解决方案

更新users表格

添加一个text名为metausers表的字段.

更新您的User模型

class User < ActiveRecord::Base  
  serialize :meta, Hash  

  def after_initialize
    self.meta ||= {} if new_record?
  end

end
Run Code Online (Sandbox Code Playgroud)

添加新的元字段

u = User.first
u.meta[:hobbies] = "skiing"
u.save
Run Code Online (Sandbox Code Playgroud)

访问元字段

puts "hobbies=#{u.meta[:hobbies]}"
Run Code Online (Sandbox Code Playgroud)

迭代元字段

u.meta.each do |k, v|
  puts "#{k}=#{v}"
end
Run Code Online (Sandbox Code Playgroud)

要满足第5个要求,您需要使用Solr或Sphinx全文搜索引擎.它们比依赖DB进行LIKE查询更有效.

如果您通过Sunspot gem使用Solr,这是一种方法.

class User    
  searchable do
    integer(:user_id, :using => :id)
    meta.each do |key, value|
      t = solr_type(value)
      send(t, key.to_sym) {value} if t
    end
  end

  def solr_type(value)
    return nil      if value.nil?
    return :integer if value.is_a?(Fixnum)
    return :float   if value.is_a?(Float)
    return :string  if value.is_a?(String)
    return :date    if value.is_a?(Date)
    return :time    if value.is_a?(Time)
  end    

  def similar_users(*args)
    keys = args.empty? ? meta.keys : [args].flatten.compact
    User.search do
      without(:user_id, id)
      any_of do
        keys.each do |key|
          value = meta[key]
          with(key, value) if value
        end
      and
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

查找类似用户

u = User.first
u.similar_users # matching any one of the meta fields
u.similar_users :hobbies # with matching hobbies
u.similar_users :hobbies, :city # with matching hobbies or the same city
Run Code Online (Sandbox Code Playgroud)

这里的性能提升很重要.