Rails验证:多字段唯一性:数据库中不必要的查询

时间:2016-02-24 16:04:20

标签: postgresql validation ruby-on-rails-4 unique-constraint

我有一个Rails 4.2 / Ruby 2.2应用程序,我有一个用户表和一个服务器表。服务器“属于”用户(由 user_id 键入的外键),并且服务器名称对于特定用户必须是唯一的。在PostgreSQL中看起来像这样:

CREATE TABLE users (
  id integer NOT NULL,
  name character varying(255)
);

CREATE TABLE servers (
  id integer NOT NULL, 
  name character varying(255),
  user_id integer NOT NULL
);

ALTER TABLE servers ADD CONSTRAINT servers_user_id_fk FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE RESTRICT; 

ALTER TABLE servers ADD PRIMARY KEY (user_id, name);

此SQL将阻止我添加引用无效用户的服务器,并且它将阻止给定用户多次使用服务器名称。

Rails模型看起来像这样:

class User << ActiveRecord::Base
  has_many :servers
end

class Server << ActiveRecord::Base
  belongs_to :user, foreign_key: "user_id"

  validates :must_have_unique_name_and_user_id, on: :create

  def must_have_unique_name_and_user_id
    name_count = Server.where(user_id: user_id, name: name).count
    if name_count > 0
      errors[:base] << "Server name is already taken for name #{name}, and user #{user.name}"
  end
end

我的问题是,当我运行带有代码覆盖率的测试套件时, must_have_unique_name_and_user_id()会被调用数百次,因为这两个模型是我整个数据模型的基础。这些验证调用中的每一个都会导致数据库查询,以检查服务器名称是否对用户是唯一的。

这似乎是很多不必要的工作,只是为了确保Rails中的有效数据,无论如何数据库都不允许这样做。

是否可以构造Rails验证,以便在用户尝试为一个复制(user_id,name)组合时显示Rails验证 must_have_unique_name_and_user_id()中的错误消息服务器,但是每次都不运行Rails验证?

1 个答案:

答案 0 :(得分:1)

数据库是您的用户和服务器一起出现的真实来源,因此Rails无法在不询问的情况下对其进行验证。

但是,您可以删除Rails中的验证,并通过在return上添加唯一索引将其保留到数据库中。如果您尝试更新或创建无效行,则会引发异常,然后您必须捕获并处理这些行。

因此,您的选择是额外查询和代码复杂性之间的权衡。我的建议是:让Rails执行额外的查询,直到遇到性能问题。

如果这是特定于您的测试套件的问题(即,您试图让它运行得更快),您可以尝试避免在测试中实际创建对象。与在一个进程的内存中执行所有操作相比,触摸数据库通常是一个很大的打击,因此如果您可以构建,连接和使用对象而不保存它们,您可能会获得稳定的加速。