与Active_to对象

时间:2016-03-15 16:18:08

标签: ruby-on-rails activerecord

我想知道是否有办法在ActiveRecord :: Relation中访问关联的belongs_to对象

例如在这种关系中:

class Customer < ActiveRecord::Base
  has_many :invoices
end


class Invoice < ActiveRecord::Base
  belongs_to :customer

  def self.check_new
    # do something
  rescue => e
    MyLogger.log_error_for(customer, __method__)
  end
end

所以,如果我打电话

Customer.first.invoices.check_new

并发生错误。我想为此特定客户记录此错误。

我知道我可以在类方法中使用scoped获取发票并调用scoped.first.customer。 但这看起来有点脏。此外,如果还没有任何发票,这将不是一个解决方案。

如果有办法可以做到这一点,我不是一般的,但是对scoped进行检查会给你带来类似的东西:

SELECT * FROM SOME JOIN WHERE customer_id = 1

所以有一种对Customer对象的引用。 有人可以帮忙吗?

更新: 这只是一个例子。其实我不做记录或其他什么。当然,他们是更好的记录方式。 我想要的只是在模型类中放置更新集合的代码。像普通的Customer.first.invoices.create一样。并且可以访问此关系的父级。

2 个答案:

答案 0 :(得分:0)

可能是这样的:

  customer = Customer.first
  customer.invoices.each {|invoice| invoice.check_new}

其他一些想法:

更新:

我认为Customer.first.invoices.check_new会抛出错误,因为你试图在集合上调用类方法。

^^^

这是不正确的。请参阅oh-my-zsh

你可以做你想要的。我不知道。这看起来真的很复杂。我相信你有一个有效的用例。但是,目前尚不清楚它是什么。我仍然可能会这样做:

  customer = Customer.first
  customer.invoices.check_new

回答您的问题,如果不符合您的使用案例,请道歉。

END UPDATE。

因此,.check_new应该是一个实例方法。有点像:

  class Invoice < ActiveRecord::Base

    def check_new
      if errors = check_for_errors
        MyLogger.log_error_for(customer, __method__, errors)
      end
    end

    private 

    def check_for_errors
      ... error checking logic
    end

  end       

为了让你的模型变得模糊,你可以创建一个普通的旧Ruby对象,如:

  #app/managers/invoice_manager.rb
  class InvoiceManager

    class << self

      def check_new(invoices)
        invoices.each do |invoice|
          @invoice = invoice
          if @errors = invoice_errors
            log_errors
          end
        end
      end

      private

      def invoice_errors
        ... invoice error checking logic
      end

      def log_errors
        MyLogger.log_error_for(invoice.customer, caller[0][/`.*'/][1..-2], @errors)
      end

    end 

  end

在这种情况下,您可以执行以下操作:

  InvoiceManager.check_new(Customer.first.invoices)

这样,您的Invoice模型又回归到处理数据库交互,并且您的业务逻辑完全分离。

此外,使用此方法,您的控制器根本不需要访问Invoice类或其实例。或者,对Invoice课程一无所知。这样可以改善ControllerModel图层之间的分隔。并使潜在的下游变更Invoice更具破坏性。

最后,我发现测试PORO比测试控制器容易得多。

答案 1 :(得分:0)

你有点困惑。

这是一个工作示例,为简单起见,使用控制器

WhateverController

def show  #(no matter which one, could be index or anything with GET request)
  @user = Customer.first
  @invoice = @user.invoices.first   #works with the properly set up belongs_to and has_many associations which you did well
  @invoice.check_new  #it is called on an instance (object) of invoice class, not on the class it self as you do
end

班级发票

 def check_new  #if you put self before the method name then it is a class method and self is equal to Invoice (class name), if you don't then it's an instance method
   self.do_something  #now self means the instance itself which is the single @invoice at the moment. If you use self within the class method then this self would mean Invoice class just like in the name of the method
 end

基本上,当您使用类本身时,您使用类方法,并且您不会尝试对单个实例执行任何操作。例如

@invoices = Invoice.limit(10).order(create_at: :desc) 

在这里,您尝试查找该类的一些实例,因此您无法在单个实例上使用方法。但是,让我们说你想修改一些实例的属性。所以你必须转到那个实例并改变它:

@invoices.each do |invoice|
  invoice.check_new
  invoice.amount = 10
end

这里一次更改一个实例,因此需要实例方法。

我建议点击谷歌并查看差异。这在rails

中非常重要