代码之家  ›  专栏  ›  技术社区  ›  Joshua Partogi

如何在Rails ActiveRecord的模型中为属性创建默认值?[复制品]

  •  180
  • Joshua Partogi  · 技术社区  · 16 年前

    这个问题已经有了答案:

    我想通过在ActiveRecord中定义属性来为其创建默认值。默认情况下,每次创建记录时,我都希望具有属性的默认值 :status . 我试着这样做:

    class Task < ActiveRecord::Base
      def status=(status)
        status = 'P'
        write_attribute(:status, status)
      end
    end
    

    但创建后,我仍然从数据库中检索此错误:

    ActiveRecord::StatementInvalid: Mysql::Error: Column 'status' cannot be null
    

    因此,我假定该值不适用于该属性。

    在Rails中,优雅的方式是什么?

    多谢。

    11 回复  |  直到 9 年前
        1
  •  275
  •   BenKoshy Mayinx    9 年前

    可以为迁移中的列设置默认选项

    ....
    add_column :status, :string, :default => "P"
    ....
    

    或

    你可以使用回拨, before_save

    class Task < ActiveRecord::Base
      before_save :default_values
      def default_values
        self.status ||= 'P' # note self.status = 'P' if self.status.nil? might be safer (per @frontendbeauty)
      end
    end
    
        2
  •  187
  •   BeepDog    15 年前

    因为我刚刚遇到这个问题,而且Rails3.0的选项有点不同,所以我将为这个问题提供另一个答案。

    在Rails 3.0中,您希望执行如下操作:

    class MyModel < ActiveRecord::Base
      after_initialize :default_values
    
      private
        def default_values
          self.name ||= "default value"
        end
    end
    
        3
  •  79
  •   Brad The App Guy    9 年前

    当我需要默认值时,在呈现新操作的视图之前,它通常用于新记录。以下方法将仅为新记录设置默认值,以便在呈现表单时可用。 before_save 和 before_create 为时已晚 不会工作的 如果希望在输入字段中显示默认值 .

    after_initialize do
      if self.new_record?
        # values will be available for new record forms.
        self.status = 'P'
        self.featured = true
      end
    end
    
        4
  •  77
  •   Daniel Kristensen    16 年前

    您可以在不编写任何代码的情况下完成此操作:)您只需要为数据库中的列设置默认值。你可以在迁移中这样做。例如:

    create_table :projects do |t|
      t.string :status, :null => false, :default => 'P'
      ...
      t.timestamps
    end
    

    希望有帮助。

        5
  •  23
  •   EmFi    16 年前

    解决办法取决于一些事情。

    默认值是否依赖于创建时可用的其他信息? 您能用最小的结果擦除数据库吗?

    如果你回答了第一个问题“是”,那么你就要使用吉姆的解决方案。

    如果你回答了第二个问题“是”,那么你就要使用丹尼尔的解决方案。

    如果您对这两个问题都回答“否”,那么最好添加并运行一个新的迁移。

    class AddDefaultMigration < ActiveRecord::Migration
      def self.up
         change_column :tasks, :status, :string, :default => default_value, :null => false
      end
    end
    

    :字符串可以替换为ActiveRecord::Migration可以识别的任何类型。

    CPU很便宜,因此在Jim的解决方案中重新定义任务不会导致很多问题。尤其是在生产环境中。这种迁移是在加载它时进行迁移的正确方法,而且调用频率要少得多。

        6
  •  12
  •   PeppyHeppy    14 年前

    我会考虑使用发现的属性默认值 here . 你最狂野的梦想会实现。

        7
  •  9
  •   Community Mohan Dere    9 年前

    只是加强 Jim's answer

    使用 presence 一个人能做的

    class Task < ActiveRecord::Base
      before_save :default_values
      def default_values
        self.status = status.presence || 'P'
      end
    end
    
        8
  •  4
  •   aec    13 年前

    对于列类型,Rails支持开箱即用的方式——就像这个问题中的字符串一样——最好的方法是在数据库本身中设置列默认值,正如DanielKristensen所指出的那样。Rails将对数据库进行内省,并相应地初始化对象。另外,这使得你的数据库可以安全地防止有人在你的Rails应用程序之外添加一行,而忘记初始化该列。

    对于列类型,Rails不支持开箱即用(例如枚举列),Rails将无法反省列默认值。对于这些情况 不 希望在初始化之后(每次从数据库加载对象以及每次使用.new创建对象时调用)、创建之前(因为它在验证后发生)或保存之前(因为它也在更新时发生,这通常不是您想要的)使用。

    相反,您希望在“创建”上的“验证前”中设置属性,如下所示:

    before_validation :set_status_because_rails_cannot, on: :create
    
    def set_status_because_rails_cannot
      self.status ||= 'P'
    end
    
        9
  •  4
  •   Peter P.    12 年前

    如我所见,在需要默认值时有两个问题需要解决。

    1. 初始化新对象时需要存在的值。使用after-initialize不合适,因为如前所述,它将在调用find时调用,这将导致性能下降。
    2. 保存时需要保留默认值

    这是我的解决方案:

    # the reader providers a default if nil
    # but this wont work when saved
    def status
      read_attribute(:status) || "P"
    end
    
    # so, define a before_validation callback
    before_validation :set_defaults
    protected
    def set_defaults
      # if a non-default status has been assigned, it will remain
      # if no value has been assigned, the reader will return the default and assign it
      # this keeps the default logic DRY
      status = status
    end
    

    我很想知道人们为什么会想到这种方法。

        10
  •  0
  •   Joshua Partogi    16 年前

    我现在找到了一个更好的方法:

    def status=(value) 
      self[:status] = 'P' 
    end 
    

    在Ruby中,方法调用不允许有括号,因此我应该将局部变量命名为其他变量,否则Ruby会将其识别为方法调用。

        11
  •  0
  •   Matt Connolly    14 年前

    在这里的示例中,您正在将“p”分配给局部变量“status”。尝试 self.status = 'P' 为了这个王者。虽然最好用 after_initialize 如其他答案所述。

    推荐文章