Reputation: 3953
I have a activerecord model with a validates :body, presence: true. When a form is filled and submited, I get {:body=>["can't be blank"]} even though the body is not actually blank. If I remove the validation from the model, and re-submit, the record is successfully created. Why is the validation saying the body field is blank when it is not blank.
The controller.
class SqlTemplatesController < ApplicationController
def create
@sql_template = SqlTemplate.new(sql_template_params)
respond_to do |format|
if @sql_template.save
format.html { redirect_to @sql_template, notice: 'Sql template was successfully created.' }
end
end
private
def sql_template_params
params.require(:sql_template).permit(:body, :path, :format, :locale, :handler, :partial)
end
end
The Model
class SqlTemplate < ActiveRecord::Base
validates :body, :path, presence: true
validates :format, inclusion: Mime::SET.symbols.map(&:to_s)
validates :locale, inclusion: I18n.available_locales.map(&:to_s)
validates :handler, inclusion: ActionView::Template::Handlers.extensions.map(&:to_s)
def to_liquid
SqlTemplateDrop.new(self)
end
def body=(text)
if self[:handler] == 'liquid'
@template = Liquid::Template.parse(text)
self[:body] = Marshal.dump(@template)
end
end
def render(options = {})
template.render options
end
private
def template
return @body unless @body.nil?
@body = Marshal.load(self[:body])
end
end
In the rails console If create a new record and set the body field to either a string body => "message body" or a liquid markup eg body => "{{body}}, it will raise the error {:body=>["can't be blank"]} but if remove the validation they work.
irb(main):016:0> a = SqlTemplate.create(:body => "{{body}", path => "mail/liquid_database_template", :format => "html", :locale => "en", :handler => "liquid", :partial => false)
(0.1ms) begin transaction
(0.1ms) rollback transaction
irb(main):016:0> a.errors.messages
=> {:body=>["can't be blank"]}
If I remove the validation and submit a form, it all works, as shown below:
Started POST "/sql_templates" for 127.0.0.1 at 2014-03-11 15:28:14 +0000
Processing by SqlTemplatesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"GVsRbsCKSlcEMiL1AzXE0tT8LBCNhhoK6wSGzvnB80A=", "sql_template"=>{"body"=>"{{body}}", "path"=>"customer_mail/liquid_database_template", "format"=>"html", "locale"=>"en", "handler"=>"liquid", "partial"=>"0"}, "commit"=>"Create Sql template"}
#<SqlTemplate id: nil, body: nil, path: "customer_mail/liquid_database_template", format: "html", locale: "en", handler: "liquid", partial: false, created_at: nil, updated_at: nil>
(0.1ms) begin transaction
SQL (9.4ms) INSERT INTO "sql_templates" ("created_at", "format", "handler", "locale", "path", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["created_at", "2014-03-11 15:28:14.869619"], ["format", "html"], ["handler", "liquid"], ["locale", "en"], ["path", "customer_mail/liquid_database_template"], ["updated_at", "2014-03-11 15:28:14.869619"]]
(621.4ms) commit transaction
Redirected to http://localhost:3000/sql_templates/7
Completed 302 Found in 662ms (ActiveRecord: 630.9ms)
Started GET "/sql_templates/7" for 127.0.0.1 at 2014-03-11 15:28:15 +0000
Processing by SqlTemplatesController#show as HTML
Parameters: {"id"=>"7"}
SqlTemplate Load (3.1ms) SELECT "sql_templates".* FROM "sql_templates" WHERE "sql_templates"."id" = ? LIMIT 1 [["id", 7]]
Rendered sql_templates/show.html.erb within layouts/application (11.4ms)
Completed 200 OK in 52ms (Views: 46.0ms | ActiveRecord: 3.1ms)
If I add the validation back and submit it fails as shown below:
Started POST "/sql_templates" for 127.0.0.1 at 2014-03-11 14:34:22 +0000
ActiveRecord::SchemaMigration Load (0.3ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by SqlTemplatesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"GVsRbsCKSlcEMiL1AzXE0tT8LBCNhhoK6wSGzvnB80A=", "sql_template"=> {"body"=>"{{body}}", "path"=>"customer_mail/liquid_database_template", "format"=>"html", "locale"=>"en", "handler"=>"liquid", "partial"=>"0"}, "commit"=>"Create Sql template"}
#<SqlTemplate id: nil, body: nil, path: "customer_mail/liquid_database_template", format: "html", locale: "en", handler: "liquid", partial: false, created_at: nil, updated_at: nil>
(0.2ms) begin transaction
(0.2ms) rollback transaction
Rendered sql_templates/_form.html.erb (32.6ms)
Rendered sql_templates/new.html.erb within layouts/application (57.1ms)
Completed 200 OK in 208ms (Views: 143.0ms | ActiveRecord: 1.4ms)
Upvotes: 1
Views: 1987
Reputation: 3953
I resolved it myself. The problem was with the setter method for the body field. Normally rails automatically defines this but if it is being overridden to do some extra things which is the case here, I still needed to set self[:body] = text at the top before everything else. Note that text is the value passed in to the field through the form. Without that validates presence will fail.
def body=(text)
self[:body] = text
end
Upvotes: 0
Reputation: 29349
Your body
setter is getting called before the setter for handler
. So self[:handler]
will be nil when it goes into the body=
method
You can try one of these
i) Change the order of your hash
a = SqlTemplate.create(:handler => "liquid", :body => "{{body}", path => "mail/liquid_database_template", :format => "html", :locale => "en", :partial => false)
ii) Set the body later after setting the handler
a = SqlTemplate.new(:handler => "liquid", path => "mail/liquid_database_template", :format => "html", :locale => "en", :partial => false)
a.body = "{{body}}"
a.save
Upvotes: 1