#ruby-on-rails
Вопрос:
Версия Rails — 6.0.3.6
У меня есть одна таблица subscription_attempts, и в ней есть один столбец attempt_token
, который является уникальным случайным маркером для каждой записи, и он генерируется на основе has_secure_token rails.
def random_method(value)
find_by(id: value) || find_by(attempt_token: value)
end
Все работало нормально, пока я не получил одну попытку, которая начинается с цифры. Вот это значение attempt_token — 5HqBToVVbFVL4T6TLzuuKju8
Когда я использую этот метод, он выдает мне запись о подписке с идентификатором 5. Я также попробовал с помощью where, и результат тот же.
Когда я попытался выполнить этот запрос с исключением where got ниже
SubscriptionAttempt.where('id = ?', '5HqBToVVbFVL4T6TLzuuKju8')
ActiveRecord::StatementInvalid (PG::InvalidTextRepresentation: ERROR: invalid input syntax for type bigint: "5HqBToVVbFVL4T6TLzuuKju8")
LINE 1: ...empts".* FROM "subscription_attempts" WHERE (id = '5HqBToVVb...
Я предполагаю, что эти методы должны вести себя не так. Это должно дать nil
результат, если значение не совпадает со значением базы данных. Может кто-нибудь, пожалуйста, дать мне решение этой проблемы?
Ответ №1:
ActiveRecord автоматически выполняет приведение типов перед запросом к базе данных. Вы можете прочитать больше об этом здесь. В принципе, поскольку ActiveRecord знает, что id
столбец является целым числом, он приведет value
переданное find_by(id: value)
значение к целому числу и:
> '5HqBToVVbFVL4T6TLzuuKju8'.to_i
=> 5
Попробуйте изменить порядок:
def random_method(value)
find_by(attempt_token: value) || find_by(id: value)
end
Это все еще не является 100% пуленепробиваемым в случае, если у вас есть экземпляр с идентификатором attempt_token
"1234"
и другой экземпляр с идентификатором 1234
. Первое всегда будет возвращено, даже если вы намеревались забрать второе.
Другим решением было бы добавить постоянный префикс к вашему attempt_token
:
ATTEMPT_TOKEN_PREFIX = 'at_'.freeze
def attempt_token_with_prefix
# provide this value to the user instead of just providing the raw attempt_token
"#{ATTEMPT_TOKEN_PREFIX}#{attempt_token}"
end
def random_method(value)
if value.starts_with?(ATTEMPT_TOKEN_PREFIX)
find_by(attempt_token: value[ATTEMPT_TOKEN_PREFIX.length..])
else
find_by(id: value)
end
end
Комментарии:
1. Я справлялся с этим по-другому
value =~ /Ad Z/ ? find_by(id: value) : find_by(attempt_token: value)
.