Ruby模型的数组属性

时间:2011-11-11 17:32:21

标签: ruby database-design attributes migration ruby-on-rails-3.1

是否可以为作为数组的类创建属性?我试过阅读this,但我并没有从中得到很多。我想做这样的事情:

class CreateArches < ActiveRecord::Migration
  def change
    create_table :arches do |t|
      t.string :name
      t.array :thearray
      t.timestamps
    end
  end
end

这样当我在Arch的一个实例上调用.thearray时,我会得到一个可以添加新元素的数组。

ruby-1.9.2-p290 :006 > arc = Arch.new
ruby-1.9.2-p290 :007 > arc.thearray
 => [] 

5 个答案:

答案 0 :(得分:48)

使用文本字段创建模型

> rails g model Arches thearray:text
  invoke  active_record
  create    db/migrate/20111111174052_create_arches.rb
  create    app/models/arches.rb
  invoke    test_unit
  create      test/unit/arches_test.rb
  create      test/fixtures/arches.yml
> rake db:migrate
==  CreateArches: migrating ===================================================
-- create_table(:arches)
   -> 0.0012s
==  CreateArches: migrated (0.0013s) ==========================================

编辑模型以将字段序列化为数组

class Arches < ActiveRecord::Base
  serialize :thearray,Array
end

测试出来

ruby-1.8.7-p299 :001 > a = Arches.new
 => #<Arches id: nil, thearray: [], created_at: nil, updated_at: nil> 
ruby-1.8.7-p299 :002 > a.thearray
 => [] 
ruby-1.8.7-p299 :003 > a.thearray << "test"
 => ["test"] 

答案 1 :(得分:10)

虽然你可以像tokland建议的那样使用序列化数组,但这在关系数据库中很少是一个好主意。你有三个更好的选择:

  • 如果数组包含实体对象,则可能最好将其建模为has_many关系。
  • 如果数组实际上只是一个数组值,那么您可能希望将每个值放在一个单独的字段中并使用composed_of
  • 如果您要使用大量不是has_many的数组值,您可能需要调查实际支持数组字段的数据库。 PostgreSQL执行此操作(并且Rails 4迁移中支持数组字段),但您可能希望使用像MongoDB这样的非SQL数据库或MagLev之类的对象持久性。

如果您可以描述您的用例 - 也就是说,您在阵列中获得了哪些数据 - 我们可以尝试帮助确定最佳行动方案。

答案 2 :(得分:6)

迁移:

t.text :thearray, :default => [].to_yaml

在模型中使用serialize

class MyModel
  serialize :thearray, Array
  ...
end

正如Marnen在他的回答中所说,知道要在该数组中存储哪种信息会很好,序列化属性可能不是最佳选择。

[Marten Veldthuis'警告]小心改变序列化阵列。如果您直接更改它:

my_model.thearray = [1,2,3]

工作正常,但如果你这样做:

my_model.thearray << 4

然后ActiveRecord不会检测到阵列的值已经改变。要告诉AR有关此更改,您需要执行此操作:

my_model.thearray_will_change!
my_model.thearray << 4

答案 3 :(得分:0)

如果使用Postgres,则可以使用其Array feature

迁移:

add_column :model, :attribute, :text, array: true, default: []

然后像数组一样使用它:

model.attribute # []
model.attribute = ["d"] #["d"]
model.attribute << "e" # ["d", "e"]

Marnen提到了这种方法,但是我相信这里有个例子会有所帮助。

答案 4 :(得分:0)

栏位6 +

在Rails 6(和次要程度的Rails 5)中,您可以使用Attribute API,这将允许您创建类型化的,“虚拟” /非数据库支持的列,甚至是默认属性。例如:

root@47cbabc35dca:/# python
Python 3.9.0 (default, Nov 18 2020, 13:28:38)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from Crypto import Random
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'Crypto'
>>>
root@47cbabc35dca:/# python -m pip install pyCrypto
Collecting pyCrypto
  Downloading pycrypto-2.6.1.tar.gz (446 kB)
     |████████████████████████████████| 446 kB 3.0 MB/s
Building wheels for collected packages: pyCrypto
  Building wheel for pyCrypto (setup.py) ... done
  Created wheel for pyCrypto: filename=pycrypto-2.6.1-cp39-cp39-linux_x86_64.whl size=521774 sha256=efc58b4ee8a1b8404b06b4d5aa75e51ebe90e6eec90e68f00b1e805582fcbc74
  Stored in directory: /root/.cache/pip/wheels/9d/29/32/8b8f22481bec8b0fbe7087927336ec167faff2ed9db849448f
Successfully built pyCrypto
Installing collected packages: pyCrypto
Successfully installed pyCrypto-2.6.1
root@47cbabc35dca:/# python
Python 3.9.0 (default, Nov 18 2020, 13:28:38)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from Crypto import Random
>>>

这将导致:

attribute :categories, :jsonb, array: true, default: [{ foo: 'bar' }, { fizz: 'buzz' }]

请注意,您可以使用任何类型,但是如果尚不可用,则必须进行注册。在上述情况下,我使用Example.new #<Example:0x00007fccda7920f8> { "id" => nil, "created_at" => nil, "updated_at" => nil, "categories" => [ [0] { "foo" => "bar" }, [1] { "fizz" => "buzz" } ] } 作为数据库,并且已经将PostgeSQL注册为类型。