Rails - 我应该在模型级别还是在db级别中定义默认属性值?

时间:2016-05-24 07:25:54

标签: ruby-on-rails ruby

在rails中,我们可以使用gem'default_value_for'或selfelf中的一个宏语句在模型级别定义默认属性值。另一方面,我们可以使用迁移'default'选项在数据库级别定义它。我很困惑哪个是rails的最佳实践,或者我们应该在不同场景中使用哪种方式?

任何回复都表示赞赏! :d

3 个答案:

答案 0 :(得分:2)

设置数据库中的默认值通常更可取,因为ActiveRecord是围绕约定优于配置方法构建的,其中数据库表驱动模型 - 而不是相反。

但是,数据库默认值不是非常智能 - 它们将始终应用并始终具有相同的静态值*(除非您更改数据库架构)。在大多数情况下,这并不重要。

但是,如果默认值需要某种计算,例如通过基于IP的地理位置设置用户的默认国家/地区,则需要在应用程序级别(模型)上设置默认值。

其他例子如下:

  • 存储序列化数据的列(不是本机JSON列)
  • 应该默认为某个记录的关系。

您在应用程序中的哪个位置设置默认值?

在模型中

最简单的例子是使用模型回调:

class Thing 
  after_initialize :set_defaults, if: :new_record?

  private
    def set_defaults
      self.foo = 'bar'
    end
end

模型回调的结论是,很难准确控制应用程序流中的哪个位置。例如,如果初始化块很昂贵,那么您不希望它在所有测试中发生。

在控制器中

如果默认值依赖于请求的上下文,例如此示例使用Geocoder进行基于IP的地理位置:

class User < ActiveRecord::Base
  def set_default_location(geo)
    u.city    = geo.city
    u.zipcode = geo.postal_code
    u.country = geo.country_code
  end
end

class UserController
  def new
    @user = User.new do |u|
      u.set_default_location(request.safe_location)
    end
  end
end

如果不仔细检查,这可能会导致控制器膨胀。

在PORO /工厂。

有人会争辩说,将过多的业务逻辑放在ORM类(ActiveRecord::Base的子类)中会导致违反单一责任原则,并使您的应用程序过于脆弱。

module ManagerFactory
  def self.new(attributes = {})
    user = User.new(attributes)
    user.add_role(:manager)
    user
  end
end

答案 1 :(得分:1)

您可能想要考虑的事情。

在应用程序中设置默认值时:

  • 可以轻松更改默认值
  • 默认可以是动态的
  • 有办法绕过默认

在数据库中设置默认值时:

  • 更改默认值需要迁移(对于大型表可能存在问题)
  • 实施动态默认值很难(或不可能)
  • 无法绕过应用程序中的默认值(更安全)

您可能需要考虑的另一个选项是覆盖属性getter方法。此方法仅在从数据库返回空(或无效值)时使用默认值,它不会更改存储新值的方式。

def foo
  read_attribute(:foo) || 'a default value'
end

答案 2 :(得分:0)

我会说何时使用它以及何时不根据您想要设置为默认值的数据使用它。

如果您想到的是要设置为默认值的静态值,例如:false, 0, user,如果您想要将某些动态设置为默认值。生命随机generated UUID或相对于创建日期而改变的日期,您可以使用default_value_for gem。