我有一个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验证?
答案 0 :(得分:1)
数据库是您的用户和服务器一起出现的真实来源,因此Rails无法在不询问的情况下对其进行验证。
但是,您可以删除Rails中的验证,并通过在return
上添加唯一索引将其保留到数据库中。如果您尝试更新或创建无效行,则会引发异常,然后您必须捕获并处理这些行。
因此,您的选择是额外查询和代码复杂性之间的权衡。我的建议是:让Rails执行额外的查询,直到遇到性能问题。
如果这是特定于您的测试套件的问题(即,您试图让它运行得更快),您可以尝试避免在测试中实际创建对象。与在一个进程的内存中执行所有操作相比,触摸数据库通常是一个很大的打击,因此如果您可以构建,连接和使用对象而不保存它们,您可能会获得稳定的加速。