2014-04-16 1 views
0

음과 레일의 검증 도우미 반환합니다 (레일 3.2.13) :내가 모델이 <em>단순화 된 버전</em>있어, 거짓

class Transfer < ActiveRecord::Base 
    attr_accessible :from,:to,:total 
    validates_presence_of :from,:to,:total 

    before_validation :positive_numbers, on: :create 
    before_validation :check_enough_balance, on: :create 
    after_validation :update_balances 

    private 
    def positive_numbers 
    unless self.total>0 
     errors.add(:total,"should be greater than 0") 
     return false 
    end 
    end 
    def check_enough_balance 
    @sender=User.find(self.from) 
    @receiver=User.find(self.to) 
    unless @sender.enough_balance(self.total) 
     errors.add(:base,"Not enough credit") 
     return false 
    end 
    end 
    def update_balances 
    @sender.balance -= self.total 
    @receiver.balance += self.total 
    @sender.save 
    @receiver.save 
    end 
    def another_action 
    puts 'does something' 
    end 

end 

total<0는, 인스턴스가 false으로 반환 할 때마다, errors 배열이 제대로 채워지고 another_action 콜백이 호출되지 않습니다.

내가 사용하여이 같은 동작을 얻는 방법을 궁금

레일에 내장 된 검증 헬퍼, 이것은 내가 그것을 시도하는 방법이다 : 유효성 검증 도우미하지 않기 때문에,이 경우 그러나

class Transfer < ActiveRecord::Base 
    attr_accessible :from,:to,:total 
    validates_presence_of :from,:to,:total 
    validates_numericality_of :total, greater_than: 0 

    before_validation :check_enough_balance, on: :create 
    after_validation :update_balances 
    private 

    def check_enough_balance 
    @sender=User.find(self.from) 
    @receiver=User.find(self.to) 
    unless @sender.enough_balance(self.total) 
     errors.add(:base,"Not enough credit") 
     return false 
    end 
    end 
    def update_balances 
    @sender.balance -= self.total 
    @receiver.balance += self.total 
    @sender.save 
    @receiver.save 
    end 
end 
class User<ActiveRecord::Base 
    attr_accessible :username 
    validates_presence_of :username, :balance 

    def enough_balance(amount) 
    self.balance >= amount 
    end 

end 

return false 다음 사용자 지정 유효성 검사 check_enough_balance이 호출됩니다. 정확히 동일하게 작동하고 싶습니다. 유효성 검사 도우미를 사용하는 것이 다소 우아하고 간결합니다.

답변

2

before_validation 콜백은 문자 그대로 "유효성 검사를 확인하기 전에이 메서드를 실행하십시오."라는 의미입니다. another_action 전에 유효성 검사를 실행하려면 다른 콜백으로 이동하는 것이 좋습니다. 귀하의 예제를 기반으로, 나는 아마도 after_validation 콜백을 원한다고 생각하지만, 더 잘 작동하는 다른 지원되는 콜백이 있습니다. ActiveRecord::Callbacks :

after_validation :another_action, on: :create 

현재 지원되는 콜백의 전체 목록을 찾을 수 있습니다.

유효성 검사 규칙은 다른 규칙이 무엇인지에 관계없이 의미가있는 독립 실행 형 장치 여야합니다. 일반적으로 모든 유효성 검사 규칙을 실행하고 결합 된 모든 오류를 수집하여 사용자가 모든 것을 동시에 수정할 수 있도록하는 것이 가장 좋습니다.

구체적인 경우 사용자가 총 0 개를 감당할 수 있는지 확인하는 것이 완벽하게 잘 된 것 같습니다. 그것은 다른 것들과 독립적 인 다른 검증 규칙 일뿐입니다. 말했다 즉, 실제 검증에 콜백으로부터 이동하는 것을 고려 :

당신이 어떤 이유로 그 검사 (예를 들어, 성능)을 수행 할 필요가 있다면
class Transfer < ActiveRecord::Base 
    ... 
    validate :enough_balance 

    private 

    def enough_balance 
    unless User.find(self.from).enough_balance(self.total) 
     errors.add(:base, "Not enough credit") 
    end 
    end 
end 

,이 처리되지 않고 오류 조건을 다시 확인하기에 충분한 쉽게 당신은 사용자 정의에 대한 자세한 정보를 찾을 수 있습니다

unless self.total <= 0 || User.find(self.from).enough_balance(self.total) 

여기 유효성 검사기 : Active Record Validations - Custom Validators

저장 기록이 유효의 일환으로 발생해서는 안 다음에 조건을 변경하여 같은 시간에 오류가있는 ation (전후 콜백 포함). 우리는, 예를 들어, 단지 수동으로 확인하려면 레코드가 유효한 경우 우리 중 아무도 그 부작용을 싶지 :

transfer = Transfer.new(...) 
if transfer.valid? 
    # Stuff gets saved?!?! 
    ... 
end 

대신은, 관련 기록을 업데이트 할 before_saveafter_save 콜백을 사용합니다. 이러한 콜백은 ActiveRecord이 레코드를 저장할 수 있다고 결정한 경우에만 실행됩니다. save 호출의 일부로 유효성 검사가 실패하면 해당 콜백이 실행되지 않습니다.추가로 수행하는이 방법으로 저장할 때

after_save :update_balances 

또한, 그것은 대신 save 방법의 save! 방법을 사용하여 트랜잭션에 모든 것을 포장하기 위해 일반적으로 가장 좋은 방법입니다. 명시 적으로 리턴 값을 확인할 때 save을 사용해야합니다. 모든 것이 예상 한 상태라고 가정 할 때 save!을 사용해야합니다. save!에 의해 발생한 예외는 해당 트랜잭션의 일부로 수행 된 다른 모든 변경 사항을 롤백하는 데 사용될 수 있습니다.

몇 가지 예 :

transfer = Transfer.new(...) 

if transfer.save # Good! 
    ... 

if transfer.save! # Bad, causes an exception when you might expect false 
    ... 

transfer.save # Bad, can silently fail 

transfer.save! # Good, raises an exception if it unexpectedly fails 

# All changes will be rolled back if any of the `save!` calls raise exceptions 
Transfer.transaction do 
    transfer.save! 
    something_else.save! 
    yet_another_thing.save! 
end 

당신은 일반적으로 transfer.save 또는 transfer.save! 호출 주위 컨트롤러에 Transfer.transaction 전화를 넣어 것입니다. 트랜잭션에 대한

자세한 내용은 여기에서 찾을 수 있습니다 : 좋은 생각이다 ActiveRecord::Transactions::ClassMethods

+0

합니다. 그러나 ** 유효성 검사를하기 전에 콜백이 필요하다는 것을 잊었습니다. 예제를 편집하여 이전의'another_action'이'check_enough_balance'가되어 충분한 신용을 확인합니다. 나는 그 대답에 감사한다. – lllllll

+0

@ vint-i-vuit 나는 당신의 예제에서'check_enough_balance'를 다른 validator로 취급해야한다고 생각합니다. 자세한 내용은 내 편집을 참조하십시오. –

+0

다시 한 번 답변 드리겠습니다. 나는 예제를 확장하는 질문을 편집했다. (필자는 그것을 단순화해서는 안된다.) 왜 * 왜 * 하나가 실패하자마자 유효성 검사를 중단하기를 원한다. 당신이 볼 수 있듯이 모든 검증이 오류없이 통과하면 실행되기를 원하는'after_validation' 메서드가 있습니다. 유효성 확인 도우미를 사용하여 얻은 가장 가까운 것은'after_validation : update_users_balance, if : : all_valid'입니다. 여기서'def all_valid; self.errors.empty ?; 끝 '. 그러나 이것은 어떤 이유로 ** ** 새로운'@ transfer'를 무효화하더라도 ** 저장합니다. :/ – lllllll