Проверка Rails: уникальность нескольких полей: ненужные запросы в базе данных
У меня есть приложение 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 так, чтобы сообщение об ошибке, которое я имею в проверке Rails, must_have_unique_name_and_user_id() отображалось, если пользователь пытается создать дублированную комбинацию (user_id, name) для сервера, но так, чтобы проверка Rails не выполнялась бегать каждый раз?
1 ответ
База данных - ваш источник правды, для которого пользователи и серверы встречаются вместе, поэтому Rails не может проверить это без запроса.
Однако вы можете удалить проверку в Rails и оставить ее для БД, поместив уникальный индекс в servers.user_id, servers.name
, Это вызовет исключение, если вы попытаетесь обновить или создать недопустимую строку, которую вам затем придется перехватить и обработать.
Таким образом, ваши варианты - это компромисс между дополнительными запросами и сложностью кода. Мой совет: пусть Rails будет выполнять дополнительные запросы, пока вы не столкнетесь с проблемами производительности.
Если это проблема, характерная для вашего набора тестов (т. Е. Вы пытаетесь заставить его работать быстрее), вы можете попытаться избежать создания объектов в тестах. Прикосновение к БД часто является большим ударом по сравнению с выполнением всего в памяти одного процесса, поэтому, если вы можете создавать, связывать и использовать объекты без их сохранения, вы, вероятно, получите значительное ускорение.