【问题标题】:Array Attribute for Ruby ModelRuby 模型的数组属性
【发布时间】:2020-11-24 12:11:18
【问题描述】:

是否可以为作为数组的类创建属性?我尝试阅读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
 => [] 

【问题讨论】:

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


    【解决方案1】:

    使用文本字段创建模型

    > 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"] 
    

    【讨论】:

    • 请注意,此方法保留数组中的符号,而使用 PG 的数组列类型则不会。
    【解决方案2】:

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

    • 如果数组包含实体对象,最好将其建模为has_many 关系。
    • 如果数组实际上只是一个值数组,例如数字,那么您可能希望将每个值放在一个单独的字段中并使用composed_of
    • 如果您要使用很多不是has_manys 的数组值,您可能需要研究一个真正支持数组字段的数据库。 PostgreSQL 会执行此操作(Rails 4 迁移支持数组字段),但您可能希望使用 MongoDB 等非 SQL 数据库或 MagLev 应该提供的对象持久性。

    如果您能描述您的用例——即数组中有哪些数据——我们可以尝试帮助找出最佳的行动方案。

    【讨论】:

    • 嗯,好的,我的数组将只包含字符串。你怎么看?
    • 数组代表什么?你为什么首先拥有它?
    • 我最初写道,我不知道有任何本地存储数组或散列的 SQL 数据库。事实证明 Postgres 原生处理这两种类型,Rails 4 在迁移和 ActiveRecord 中支持它们。使用 Postgres 的又一个理由。 :)
    • @Dorian 取决于用例。这里没有足够的信息可以了解。
    【解决方案3】:

    迁移:

    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
    

    【讨论】:

    • 它将是一个字符串数组。序列化是要走的路吗?
    • @tquarton 几乎肯定不会。看我的回答。
    【解决方案4】:

    如果使用 Postgres,你可以使用它的Array feature:

    迁移:

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

    然后像数组一样使用它:

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

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

    【讨论】:

    • 请注意,使用此方法时不会保留符号
    【解决方案5】:

    Rails 6+

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

    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作为数据库并且已经注册了:jsonb作为一个类型。

    【讨论】:

      猜你喜欢
      • 2011-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多