【问题标题】:Rails 4 Postgres Array attribute: how can I ensure that the attribute is an array during validation?Rails 4 Postgres Array 属性:如何在验证期间确保该属性是一个数组?
【发布时间】:2014-04-11 14:54:53
【问题描述】:

我刚开始在 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,就好像它从未见过字符串输入一样。

【问题讨论】:

    标签: ruby-on-rails arrays postgresql validation


    【解决方案1】:

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

    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)
    

    但不能用于数组类型 email[]。

    所以使用检查。

    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
    

    【讨论】:

      【解决方案2】:

      所以在花了好几个小时试图弄清楚我是否做错了什么之后,我凭直觉认为这是 Rails 中的一个错误,我在 Rails GitHub 存储库中打开了一个问题:https://github.com/rails/rails/issues/14716

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

      【讨论】:

        猜你喜欢
        • 2020-12-30
        • 2020-11-03
        • 2015-07-04
        • 2011-03-17
        • 1970-01-01
        • 2011-06-15
        • 1970-01-01
        • 2016-01-24
        • 2020-03-09
        相关资源
        最近更新 更多