Model.valid? = false, the model still saves

I have a model w/ three validations in PaymentApplication:

validate :total_applications_cannot_exceed_payment_amount
validate :cannot_exceed_activity_price
validates_numericality_of :amount

I can confirm that one of them is firing, and that
payment_application.valid? is returning false after calling the ‘save’
method on a new payment_application. The record, however, is getting
saved to the database. I’d like it to not save.

Am I missing something simple?

2009/9/14 Kendall B. [email protected]:

saved to the database. I’d like it to not save.

Am I missing something simple?

Are you sure it is getting saved? Are you checking the return from
save, it should return false if the save fails. If you still think it
is not functioning correctly post the relevant section of controller
code here.

Colin

Colin L. wrote:

2009/9/14 Kendall B. [email protected]:

saved to the database. I’d like it to not save.

Am I missing something simple?

Are you sure it is getting saved? Are you checking the return from
save, it should return false if the save fails. If you still think it
is not functioning correctly post the relevant section of controller
code here.

Colin

Sure.

Yeah, it’s getting saved by an INSERT statement. Here’s the controller
code:

def create
@activity = Activity.find(params[:activity_id])
@payment_application =
@activity.payment_applications.new(params[:payment_application])

respond_to do |format|
  if @payment_application.save
    format.html { redirect_to(@payment_application) }
    format.js { index }
    format.apply_to_activity { index }
    format.xml  { render :xml => @payment_application, :status => 

:created, :location => @payment_application }
else
format.html { render :action => “new” }
format.js { render :action => “new”, :status =>
:unprocessable_entity }
format.apply_to_activity { index }
format.xml { render :xml => @payment_application.errors,
:status => :unprocessable_entity }
end
end
end

And the model code:

class PaymentApplication < ActiveRecord::Base
belongs_to :activity
belongs_to :payment

after_create :apply_full, :if => :apply_full_payment

attr_accessor :apply_full_payment
attr_accessor :amount_as_str

acts_as_ferret :fields => { :payment_id => { :store => :yes },
:check_num => { :store => :yes },
:comments => { :store => :yes } }

validate :total_applications_cannot_exceed_payment_amount
validate :cannot_exceed_activity_price
validates_numericality_of :amount

def amount=(value)
unless value.blank?
write_attribute(:amount, Accounting.string_as_int(value))
else
write_attribute(:amount, nil)
end
end

protected

def apply_full
  write_attribute(:amount, self.payment.amount)
  write_attribute(:comments, self.payment.comments)
  self.save
end

def total_applications_cannot_exceed_payment_amount
  if self.payment
    if self.payment.payment_applications.sum(:amount) + self.amount 

self.payment.amount
self.errors.add(:amount, “of all payment applications cannot
exceed total payment amount.”)
end
end
end

def cannot_exceed_activity_price
  if self.payment
    if self.payment.payment_applications.sum(:amount) + self.amount 

self.activity.price
self.errors.add(:amount, “cannot exceed activity price.”)
end
end
end

end

Mucho thankso for the response.

2009/9/14 Kendall B. [email protected]:

code here.
  @payment_application =
@activity.payment_applications.new(params[:payment_application])

  respond_to do |format|
   if @payment_application.save

I don’t know if it is a factor but I have never tried putting the save
inside the respond_to. The first thing I would do is to take it out to
see if that is a factor. Unless you know it is not that.

How do you know that valid? is failing at this point?

Colin

Kendall B. wrote:

I have a model w/ three validations in PaymentApplication:

validate :total_applications_cannot_exceed_payment_amount
validate :cannot_exceed_activity_price
validates_numericality_of :amount

This might be a shot in the dark, I’m not completely sure what happens
when calling validate with the symbol following, but are you sure the
second call to validate override the first? I think there should be only
one custom validation method on a model that does all your custom
validations.

Try removing the second validate and add both symbols to one validate as
shown in this example:
class Invoice < ActiveRecord::Base
validate :expiration_date_cannot_be_in_the_past,
:discount_cannot_be_greater_than_total_value

def expiration_date_cannot_be_in_the_past
errors.add(:expiration_date, “can’t be in the past”) if
!expiration_date.blank? and expiration_date < Date.today
end

def discount_cannot_be_greater_than_total_value
errors.add(:discount, “can’t be greater than total value”) if
discount > total_value
end
end

Colin L. wrote:

2009/9/14 Kendall B. [email protected]:

code here.
  @payment_application =
@activity.payment_applications.new(params[:payment_application])

  respond_to do |format|
   if @payment_application.save

I don’t know if it is a factor but I have never tried putting the save
inside the respond_to. The first thing I would do is to take it out to
see if that is a factor. Unless you know it is not that.

How do you know that valid? is failing at this point?

Colin

Well, that’s pretty standard for REST controllers. Alls I do is output
@payment_application.valid? and I get ‘false’ while the record is still
getting saved.

Hi –

On Mon, 14 Sep 2009, Kendall B. wrote:

code here.

Colin

Sure.

Yeah, it’s getting saved by an INSERT statement. Here’s the controller
code:

Nothing definite springs to mind, but a couple of questions:

At the moment you’re getting the “false” result, what do the errors
look like?

Your validations have a big “if” in them (if self.payment). What’s the
status of that when the record is being saved?

You have an after_create hook. Is it possible that that hook is doing
something that’s making the record invalid after it’s already been
saved?

David


David A. Black, Director
Ruby Power and Light, LLC (http://www.rubypal.com)
Ruby/Rails training, consulting, mentoring, code review
Book: The Well-Grounded Rubyist (The Well-Grounded Rubyist)

Fantastic! Thanks so much. I changed after_create to a
before_validation, and it worked.