#ruby-on-rails #validation #model
#ruby-on-rails #проверка #Модель
Вопрос:
У меня есть следующая модель rails:
class Product < ActiveRecord::Base
end
class CreateProducts < ActiveRecord::Migration
def self.up
create_table :products do |t|
t.decimal :price
t.timestamps
end
end
def self.down
drop_table :products
end
end
Но когда я делаю следующее в консоли rails:
ruby-1.9.2-p180 :001 > product = Product.new
=> #<Product id: nil, price: nil, created_at: nil, updated_at: nil>
ruby-1.9.2-p180 :002 > product.price = 'a'
=> "a"
ruby-1.9.2-p180 :003 > product.save
=> true
ruby-1.9.2-p180 :004 > p product
#<Product id: 2, price: #<BigDecimal:39959f0,'0.0',9(9)>, created_at: "2011-05-18 02:48:10", updated_at: "2011-05-18 02:48:10">
=> #<Product id: 2, price: #<BigDecimal:3994ca8,'0.0',9(9)>, created_at: "2011-05-18 02:48:10", updated_at: "2011-05-18 02:48:10">
Как вы можете видеть, я написал ‘a’, и это сохранило 0.0 в базе данных. Почему это? Это особенно раздражает, потому что оно обходит мои проверки, например:
class Product < ActiveRecord::Base
validates :price, :format => /d.d/
end
Ответ №1:
все, что недопустимо, преобразуется в 0.0, если вы вызываете для этого to_f
"a".to_f #=> 0.0
вам нужно было бы проверить это с помощью проверок в модели
validates_numericality_of :price # at least in rails 2 i think
я не знаю, что делает проверка по формату, поэтому я не могу вам здесь помочь, но попробуйте проверить, что это число, регулярные выражения проверяются только по строкам, поэтому, если база данных является числовым полем, это может привести к путанице
: формат предназначен для таких вещей, как адреса электронной почты, логины, имена и т.д., Для проверки на наличие недопустимых символов и тому подобное
Ответ №2:
Вам нужно заново взглянуть на то, в чем ваша реальная проблема. Особенностью Rails является то, что строка автоматически преобразуется либо в соответствующее десятичное значение, либо в 0.0 иным образом.
Что происходит
1) Вы можете сохранить что угодно в поле ActiveRecord. Затем он преобразуется в соответствующий тип для базы данных.
>> product.price = "a"
=> "a"
>> product.price
=> #<BigDecimal:b63f3188,'0.0',4(4)>
>> product.price.to_s
=> "0.0"
2) Вы должны использовать правильную проверку, чтобы убедиться, что сохранены только допустимые данные. Что-то не так с сохранением значения 0? Если нет, то вам не нужна проверка.
3) Вам не нужно проверять, что число будет сохранено в базе данных. Поскольку вы объявили поле db десятичным полем, оно будет содержать ТОЛЬКО десятичные дроби (или null, если вы разрешаете полю иметь нулевые значения).
4) Ваша проверка была проверкой, ориентированной на строку. Таким образом, регулярное выражение проверки изменило 0.0 BigDecimal на «0.0», и оно прошло вашу проверку. Почему вы думаете, что ваша проверка была обойдена?
5) Почему, собственно, вы беспокоитесь о том, что другие программисты хранят строки в вашем поле price?
Вы пытаетесь избежать того, чтобы для продуктов по ошибке была установлена нулевая цена?Есть несколько способов обойти это. Вы могли бы проверять значение по мере его поступления (до его преобразования в десятичную дробь), чтобы убедиться, что его формат правильный. Смотрите раздел AR «Перезапись средств доступа по умолчанию»
Но я думаю, что это было бы беспорядочно и подвержено ошибкам. Вам пришлось бы установить объект ошибки записи из установщика или использовать флаг. И простая проверка класса не сработала бы, помните, что данные формы всегда поступают в виде строки.
Рекомендуется Вместо этого заставить пользователя подтвердить, что он намеревался установить цену в 0 для продукта, используя дополнительное поле только для AR (поле, которое не хранится в СУБД).
Например
attr_accessor :confirm_zero_price
# Validate that when the record is created, the price
# is either > 0 or (price is <= 0 amp;amp; confirm_zero_price)
validates_numericality_of :price, :greater_than => 0,
:unless => Proc.new { |s| s.confirm_zero_price},
:on => :create
Примечания Вышеуказанное — это то, что ОЧЕНЬ важно включить в ваши тесты.
Также у меня были подобные ситуации в прошлом. В результате моего опыта я теперь записываю в базу данных имя человека, который сказал, что значение действительно должно быть 0 долларов (или отрицательным), и пусть у них есть поле причины 255 символов для их обоснования. Экономит много времени позже, когда люди задаются вопросом, в чем была причина.