在Rails 2.2.2中动态添加字段到ActiveRecord模型?


3

假设我想允许管理用户通过Rails应用程序中的一个接口将字段添加到ActiveRecord模型中。我相信正常的ActiveRecord :: Migration代码足以修改AR模型的表结构(对于许多应用程序来说这不是明智的,我知道)。当然,在理论上只能添加某些类型的领域。

显然,添加(或编辑)记录到这个新修改的ActiveRecord模型的表单需要在运行时动态构建。一种常见的form_for方法不会。这个讨论表明这只能用JavaScript来完成。

http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/fc0b55fd4b2438a5

我用红宝石在过去查询对象为它的使用方法。我似乎记得它非常缓慢。我对Ruby和Rails非常了解,知道如何处理这个问题。我希望这里有人可以。我也对这个不涉及修改数据库的问题采取完全不同的方法。

  0

如果我理解正确,您需要一个管理界面,动态添加_columns_ misc。 rails DB架构中的表? 22 2月. 092009-02-22 08:40:49

  0

是的,这就是思想。由于ActiveRecord的灵活性,我认为这是可能的。 25 2月. 092009-02-25 00:48:14

3

说我想允许 管理用户通过在Rails应用程序的接口 一个字段添加到 ActiveRecord模型。

我已经通过有一个名为AdminAdditions的额外模型解决了这类问题。该表包括一个id,一个管理员用户id,一个模型名称字符串,一个类型字符串和一个默认值字符串。

我重写模型的查找和保存方法以从其admin_additions添加属性,并在更改时适当地保存它们。模型表格有一个大的文本字段,最初为空,我保存添加属性的非默认值。

本质上,视图和控制器可以假装模型的每个属性都有自己的列。这意味着form_for等一切工作。


1

为此,您可以使用Flex Attributes,但如果您希望能够通过这些新列进行搜索或排序,则必须编写(很多)自定义SQL。


1

我已经看到了作为解决方案提供的表格的动态变更/迁移很多次,但我从未真正看到它实现过。这个解决方案很少实现的原因有很多。

  • 如果表格很大,那么表格可能会/会被锁定长时间的假设是正常运行时间。
  • 为什么你的模型动态改变?模型结构需要动态改变是非常罕见的。更多的时候,这表明你正试图以一种普遍的方式来模拟某些特定的东西。
  • 这通常是一种产生“分类”模型的尝试,而不是另一种方法可以更好地解决的问题。
  • DDL语句通常不被用于日常DML需求的相同用户所允许。虽然情况可能如此,并且通常在ROR领域,但并非总是“正确”的方式。

你在这里想达到什么目的?更好地理解这个问题可能会揭示更自然的解决方案。

  0

我的目标是允许管理员用户将自定义字段(有限类型)添加到应用程序中的特定(不是全部)模型。 25 2月. 092009-02-25 00:49:56


5

要访问当前为模型定义的列,请使用columns方法 - 它会为每个列提供其名称,类型和其他信息(例如它是否为主键等)

但是,在运行时修改架构很微妙。

模式在第一次加载时由每个模型类预先加载(并从数据库驱动程序缓存)。在production模式下,Rails只能在启动时每个型号执行一次一次

  1. 为了迫使Rails的刷新其缓存的架构下完成修改,您应强制Ruby来自动重新加载受影响的模型类(相当多的Rails做什么对你来说,每次请求后,在development模式下运行时 - 见how to reload a class using remove_const followed by load。)
  2. 如果您有一个Mongrel群集,您还必须通知群集中的其他进程(它们运行在它们自己单独的内存空间中)以重新加载其模型的类(某些群集将允许您创建“重新启动”。 txt'文件,这将导致群集中的所有进程自动软重新启动,而无需代表您进行额外的工作。)

现在,这些已经说了,取决于您需要的实际问题解决你可能不需要动态地改变架构。而不是增加,比如,col2col1,并col3一些表entries(型号Entry),你可以使用一个表名为dyn_attribs,其中入境has_many :dyn_attribs,并在dyn_attribs既有key列(在这种情况下可以有值col1col2col3)和value柱(其列出了col1相应值,col2等)

因此,代替:

my_entry = Entry.find(123) 
col1 = my_entry.col1 
#do something with col1 

你可以使用:

my_entry = Entry.find(123, :include => :dyn_attribs) 
dyn_attribs = my_entry.dyn_attribs.inject(HashWithIndifferentAccess.new) { |s,a| 
    s[a.key] = a.value ; s 
} 
col1 = dyn_attribs[:col1] 
#do something with col1 

以上inject呼叫可以被分解消失在模型中,甚至到所有车型继承自一个基类,可能需要额外的,动态列/属性(见Polymorphic associations如何让几款车型共享相同的dyn_attribs表动态属性。)


UPDATE

加载项g或通过常规的HTML表单重新命名列。

假设您有一个代表具有动态属性的表的DynAttrTable模型,以及定义给定表的动态属性名称的DynAttrDef

运行:

script/generate scaffold_resource DynAttrTable name:string 
script/generate scaffold_resource DynAttrDef name:string 
rake db:migrate 

然后编辑生成的模型:

class DynAttrTable < ActiveRecord::Base 
    has_many :dyn_attr_defs 
end 

class DynAttrDef < ActiveRecord::Base 
    belongs_to :dyn_attr_table 
end 

您可以继续编辑控制器和视图like in this tutorial,与DynAttrDef更换RecipeDynAttrTable,和Ingredient

或者,使用reviewed here的任一插件自动把dyn_attr_tablesdyn_attr_defs表管理下由一个自动化接口(与所有的花里胡哨),代表您几乎为零的实施工作。

这应该让你去。

  0

这是对后端问题的一种优雅方法。我很关心浏览器支持,那么是否有一种简单的方法,使用Ruby生成的纯HTML表单(无AJAX)来动态构建表单,以便自动包含新的动态属性?谷歌搜索不是一个简单的方法 25 2月. 092009-02-25 02:34:24


2

的ActiveRecord :: Migration.add_column(用户,“电子邮件”,:字符串)

  0

ActiveRecord :: Migration.add_column(User,“email”,:string); User.reset_column_information 17 9月. 102010-09-17 11:51:25


1

如果你在做这在PostgreSQL现在你也许可以用JSON类型字段脱身,然后就什么店在JSON哈希值。