Rails 4 Postgres Array属性:如何在验证期间确保属性是一个数组?

时间:2014-04-11 14:54:53

标签: ruby-on-rails arrays postgresql validation

我刚刚开始在Rails 4中尝试使用本机Postgres数组类型,而且我遇到了一个我无法弄清楚的问题。如果数组属性的值不是数组,我希望应用程序引发错误。

之前,当我为该字段使用常规文本类型并使用Rails对其进行序列化时,我能够确保该属性是一个带有这样的自定义验证的数组:

def format_of_admin_email
  regexp = /.+@.+\..+/i
  if admin_emails.present? &&
      (!admin_emails.is_a?(Array) || admin_emails.detect { |a| a.match(regexp).nil? })
    errors[:base] << "admin_emails must be an array of valid email addresses"
  end
end

但是现在该字段是Postgres数组,看起来Rails会自动将字符串输入转换为空数组,因此验证永远不会失败。这是我的迁移:

class AddAdminEmailsToLocations < ActiveRecord::Migration
  def change
    add_column :locations, :admin_emails, :text, array: true
  end
end

如果我创建一个admin_emails是字符串的位置:

Location.create!(admin_emails: "this should fail")

它不会引发验证错误,并将admin_emails设置为空数组。

我做错了什么,或者这是Rails中的错误?

我还尝试检查输入是否是before_validation回调中的数组,并添加了puts admin_emails.present?,但它返回false,就像它从未看到过String输入一样。

2 个答案:

答案 0 :(得分:0)

我认为您可以使用postgresql检查约束有效的电子邮件列格式。 或在您的应用程序中。 对于exp:

digoal=# create domain email as text constraint ck check (value ~ '^.+@.+\..+$');
CREATE DOMAIN
digoal=# select 'digoal'::email;
ERROR:  value for domain email violates check constraint "ck"
digoal=# select 'digoal@1'::email;
ERROR:  value for domain email violates check constraint "ck"
digoal=# select 'digoal@126.com'::email;
     email      
----------------
 digoal@126.com
(1 row)

但它不能用于数组类型的电子邮件[]。

所以请使用check。

digoal=# create or replace function ck_email(i_email text[]) returns boolean as $$
declare
  v text;
begin
  foreach v in array i_email loop
    if substring(v from '.+@.+\..+') is not null then
      continue;
    else
      return false;
    end if;
  end loop; 
  return true;
end;
$$ language plpgsql strict;
CREATE FUNCTION

digoal=# create table t_email(id int, email text[] check (ck_email(email)));
CREATE TABLE
digoal=# insert into t_email values (1, array['digoal@126.com','a@e']::text[]);
ERROR:  new row for relation "t_email" violates check constraint "t_email_email_check"
DETAIL:  Failing row contains (1, {digoal@126.com,a@e}).
digoal=# insert into t_email values (1, array['digoal@126.com','a@e.com']::text[]);
INSERT 0 1

答案 1 :(得分:0)

所以在花了好几个小时试图弄清楚我做错了什么之后,我一直觉得这是Rails中的一个错误,我在Rails GitHub回购中打开了一个问题:https://github.com/rails/rails/issues/14716 < / p>

它没有被解雇,并且被标记了一些标签,这对我来说意味着Rails团队已经承认了这个错误。