为什么我不能直接访问Active Record回调中的实例变量?

时间:2015-11-12 20:57:00

标签: ruby-on-rails activerecord callback instance-variables

在Active Record回调中,我始终使用self.variable看到examples。有时,不需要self.前缀,因此他们只使用variable(请参阅此related question)。

根据我的阅读,这两个引用类实例变量的方法是使用访问器函数,而@variable将直接访问变量。在我的代码中,我尝试使用@variable并且它不起作用 - 就好像@variable尚未定义...您是否可以直接在Active Record回调中访问实例变量?< / p>

另外,我还没有为这个特定变量定义一个访问器函数,所以当我说self.variablevariable时我实际发生了什么(我已经尝试了两个,并且都在LHS之外工作作业)?

This question基本上就是我所要求的,但我不明白答案,需要更多澄清(我无法对答案发表评论,因为我是Stack Overflow的新手)。他的回答是:

  

[密码]与self.password相同。 ActiveRecord定义方法,而不是实例变量来访问与数据库相关的东西。这就是你无法使用@password访问它的原因。

但是我看到的回调方法总是在模型的类中定义,这应该让他们可以访问实例变量,对吧?他还说:

  

PS:回调只是对象的方法调用。唯一的区别是Rails会为你调用它们,而不是你自己。

那么他是否意味着回调方法不是从对象的引用中调用的?例如。而不是称为model_instance.callback_method,它只是被称为callback_method?如果它是一个类实例方法,如何在没有引用类实例的情况下找到callback_method?即使它以这种方式工作,它如何知道self.variable是什么?

2 个答案:

答案 0 :(得分:1)

  

你不能直接在Active Record回调中访问实例变量吗?

可以从任何实例方法访问实例变量,包括那些用作ActiveRecord回调的方法。

  

另外,我还没有为这个特定的变量定义一个存取函数,所以当我说self.variable或变量时实际发生了什么(我已经尝试了两者,并且两者都在LHS之外工作。分配)?

首先,了解两种语法之间的区别非常重要。 variable = 123会将值123分配给局部变量。 self.variable = 123将在self上调用名为variable=的方法,并将123作为参数传递。

> class Foo
>   def x=(value)
>     puts "Foo#x called with #{ value }"
>   end
>
>   def bar
>     x = 123       # local variable assignment, `x` only exists inside the `bar` method.
>     self.x = 456  # calls `x=` 
>   end
> end

> Foo.new.bar
Foo#x called with 456

可能有点令人困惑的是,使用隐式接收器调用的方法与引用局部变量共享相同的语法。

> class Bar
>   def x
>     "In method x"
>   end
>
>   def foo
>     # No local variable `x` has been initialized yet, so references to `x` will call the method with that name.
>     puts x           # calls method `x` and prints "In method x".
>                      # This example is equivalent to `puts self.x`.
>
>     x = "Hello"      # Now that a local variable is defined it will hide the method `x`.
>     puts x           # references the local variable and prints "Hello".
>     puts self.x      # calls the method `x` and prints "In method x".
>   end
> end

> Bar.new.foo
In method x
Hello
In method x

其次,您需要知道ActiveRecord为模型表的每一列创建访问器方法。让我们说我有一个模型User(id: integer, name: string, age: integer)。 ActiveRecord将定义实例方法 idnameage来阅读属性id=name=和{{1}设置属性。

实例变量完全是另一回事。您无法使用age=@id@name等实例变量访问数据库属性。

  

那么他是否意味着回调方法不会从对象的引用中调用?

不,ActiveRecord回调确实会调用对象上的实例方法。

答案 1 :(得分:0)

我相信你不太明白Rails属性不存储在实例变量中。您的所有属性(由数据库表模式定义)都存储在一个实例变量@attributes中,该变量旨在永远不会直接访问,因为它是Rails实现细节的一部分。

示例:

class Thing < ActiveRecord::Base
  # assume that we have a 'name' column in the database

  def foo
    # correct ways to access the name
    name
    self.name
    self[:name]

    # this contains name value and other name metadata
    # but you shouldn't use it
    @attributes[:name]

    # this holds nil as that variable has no relation to the "name" attribute
    @name
  end
end

在回调中,它的工作方式与所有其他方法相同 - 您可以访问由您定义的实例变量,但不能将数据库属性作为实例变量访问。

 class Thing < ActiveRecord::Base
  attr_accessor :secret

  after_initialize do
    @secret = '123'
  end

  before_create do
    p @secret
  end

  after_validation :on_validation
  def on_validation
    p @secret
  end
end

如果您尝试创建Thing对象,基于块的before_create回调和基于方法的after_validation回调都可以访问在此定义的实例变量@secret after_initialize回调。