首页 文章

如何在Rails activerecord模型中为属性创建默认值? [重复]

提问于
浏览
179

这个问题在这里已有答案:

我想通过在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

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

    ....
    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
    
  • 77

    因为我刚刚遇到这个问题,并且Rails 3.0的选项有点不同,我将提供另一个问题的答案 .

    在Rails 3.0中,你想做这样的事情:

    class MyModel < ActiveRecord::Base
      after_initialize :default_values
    
      private
        def default_values
          self.name ||= "default value"
        end
    end
    
  • 0

    当我需要默认值时,它通常用于渲染新操作视图之前的新记录 . 以下方法将仅为新记录设置默认值,以便在呈现表单时可用 . before_savebefore_create are too late 并且无效 if you want default values to show up in input fields .

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

    您可以在不编写任何代码的情况下执行此操作:)您只需在数据库中设置列的默认值即可 . 您可以在迁移中执行此操作 . 例如:

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

    希望有所帮助 .

  • 23

    解决方案取决于一些事情 .

    默认值是否取决于创建时可用的其他信息?你可以用最小的后果擦除数据库吗?

    如果你回答第一个问题,那么你想使用Jim的解决方案

    如果您回答第二个问题,那么您想使用Daniel的解决方案

    如果对这两个问题的回答都是“否”,那么最好添加和运行新的迁移 .

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

    :string可以替换为ActiveRecord :: Migration识别的任何类型 .

    CPU很便宜,所以在Jim的解决方案中重新定义Task不会导致很多问题 . 特别是在 生产环境 环境中 . 这种迁移是正确的方式,因为它加载它并且不常调用 .

  • 12

    我会考虑使用找到的attr_defaults here . 你最疯狂的梦想会成真 .

  • 0

    刚刚加强Jim's answer

    使用presence可以做到

    class Task < ActiveRecord::Base
      before_save :default_values
      def default_values
        self.status = status.presence || 'P'
      end
    end
    
  • 185

    对于列类型,Rails支持开箱即用 - 就像这个问题中的字符串一样 - 最好的方法是在Daniel Kristensen指出的数据库中设置列默认值 . Rails将对DB进行内省并相应地初始化对象 . 另外,这使得您的数据库安全,因为有人在您的Rails应用程序之外添加一行而忘记初始化该列 .

    对于列类型,Rails没有能够内省列默认值 . 对于这些情况,您不希望使用after_initialize(每次从DB加载对象时以及每次使用.new创建对象时调用),before_create(因为它在验证后发生)或before_save(因为它也会在更新时发生,这通常不是你想要的) .

    相反,您希望在before_validation中设置属性:create,如下所示:

    before_validation :set_status_because_rails_cannot, on: :create
    
    def set_status_because_rails_cannot
      self.status ||= 'P'
    end
    
  • 79

    正如我所看到的,当需要默认值时,需要解决两个问题 .

    • 初始化新对象时需要存在的值 . 使用after_initialize是不合适的,因为如上所述,它将在调用#find期间被调用,这将导致性能损失 .

    • 保存时需要保留默认值

    这是我的解决方案:

    # 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
    

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

  • 273

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

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

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

  • 4

    在此示例中,您将'P'分配给局部变量'status' . 试试 self.status = 'P' 这件事的王者 . 虽然如其他答案中提到的那样使用 after_initialize 会更好 .

相关问题