rails text_field / text_ara 空字符串 vs nil

rails text_field / text_ara empty string vs nil

我什至不确定我是否有问题,但我只是不喜欢我的 text_fields 和 text_areas 在数据库中保存为 empty string 而不是nil.

我正在玩空对象模式,我刚刚意识到如果我创建一个配置文件但不填写位置字段然后调用 <%= @user.profile.location.upcase %> 那么它就不会爆炸,因为位置是一个字符串即使它是空的。

这是 rails 惯例吗?如果是这样,那为什么呢?这有点奇怪,因为假设配置文件上有一个 number_of_pets attr 然后我尝试调用

<% if user.profile.number_of_pets.odd? %>
  <%= @user.profile.first_name %> has odd number of pets
<% end %>

然后它爆炸了,因为我不能打电话给 nil.odd?

和往常一样的形式,所以如果没有填写将保存为空字符串

<%= form_for @profile, url: user_profile_path do |f| %>
  <%= f.label :city %>
  <%= f.text_field :location, class: 'form-control' %>
  ......

最简单的解决方法是使用 gem,例如在此处找到的 "strip_attributes":https://github.com/rmm5t/strip_attributes

可以通过在您的模型中添加一个 before_save 回调来完成自定义解决方法,该回调采用任何空白值并将它们设置回零。

例如在您的模型中:

before_save :my_nil_maker_method_lol

[...]

def my_nil_maker_method_lol
  if self.whatever_attribute.blank?
    self.whatever_attribute=nil
  end
end

更新:

可以通过多种方式避免保存空白字段,例如上述方法,甚至可以在控制器访问数据库之前删除它们中的空白参数。

Rails 维护数据库完整性的方法是始终将此逻辑保留在您的模型中。它使维护变得更容易,并且给意外留下的空间也更少,比如如果您要在其他地方修改传入参数。

至于应该如何在模型中完成,实际上只是您作为开发人员期望获得输入的问题。您可以添加一个如上所示的回调,按照您认为合适的方式维护您的数据库,或者您可以添加一个 validates_presence_of 验证,如果该字段留空,将 return 向用户显示错误。

如果您询问是否应该完全避免将空字符串插入数据库,这实际上取决于您作为开发人员,因为在某些情况下您可能需要该信息,但在这种情况下听起来好像您要限制空字符串。

我说的是我观察到的,不一定Rails-convention。

例如,当我们执行 rails g post title content:text 时,您可能记得它没有 default: '' 用于标题(字符串)和内容(文本)。这已经暗示我没有 Rails-convention 与此有关,或者具体来说,默认情况下每个属性都允许为 NULL 或 nil。

使用 NULL 的优点是您可以识别哪些记录设置了这些属性。

假设我们有一个 API 服务器。如果客户端为我们的 API 服务器创建了一个 post,我们就知道哪些属性应该有值。假设如下:

client-1 的 POST 参数:

post: {title: 'Foo bar'}
  • 如果 NULL-allowed,将创建 Post(title: 'Foo bar', content: nil)
  • 如果默认:'',将创建一个 Post(title: 'Foo bar', content: '')

client-2 的 POST 参数:

post: {title: 'Foo bar', content: ''}
  • 如果 NULL-allowed,将创建 Post(title: 'Foo bar', content: '')
  • 如果默认:'',将创建一个 Post(title: 'Foo bar', content: '')

从上面,请注意,如果我们有默认值:'',那么我们无法知道客户端是否真的打算有一个空的 content 值,因为对于客户端 1 和客户端 2, post 的结果 content 值无论如何都会有 ''(空)。但是如果我们有 NULL-allowed 属性,那么我们仍然可以通过不在参数中传递属性来确定客户端是否打算没有 content 值。这是一个重要的 use-case.

根据您的项目和属性的用途,您可以使用 NULL-allowed 或为该属性设置默认空字符串。

现在,您遇到的 NULL-allowed 的一个主要问题是您不能保证每个值都是 String,因此您的 <%= @user.profile.location.upcase %> 会在case location 为 NULL 或 nil。

这可能有点烦人,尤其是当您有像您的示例这样的方法链时

<%= @user.profile.location.upcase.downcase %>

如果 @user.profile.location 为零,这将是一个问题,因为您必须优雅地确保它不会引发错误。如果 @user.profile 为 nil(假设您允许 @user.profile 为 nil),这也将是一个问题。通常,您将执行类似以下的操作来完成这项工作:

<% if !@user.profile.nil? && !@user.profile.location.nil? %> 
  <%= @user.profile.location.upcase.downcase %>
<% end %>

if 条件仍然可以持续很长时间,因为您有更长的链式方法来优雅地确保它不会引发任何错误。

使用 .try() 可能会 "clean" 这一点。我经常使用它,尤其是在模板文件中。解决方案将像下面这样更清晰,尽管对于那些不知道的人来说可能会造成混淆:

 <%= @user.profile.try(:location).try(:upcase).try(:downcase) %>

如果 .location.upcase 为零,它将 return 为零,并且不再引发任何 undefined method ... for NilClass