rails中的模型关联(进阶篇2)

作者:周星 发布:2017-10-07

       上次模型关联主要分享了多对多关系、多态,我们本次分享将继续上次的内容,给大家分享一下 `rails` 模型关联中的一些”小玩意“。

  1. 缓存
  2. 命名冲突
  3. 更新schema
  4. 关联关系作用域
  5. 双向关系
  6. 缓存

所有通过关联关系产生的方法都是带缓存的,缓存会默认保存最近一次查询的结果。

customer.orders                 # 去查数据库
customer.orders.size            # 直接读缓存
customer.orders.empty?          # 直接读缓存

但是如果你想重新读取数据,而不是使用之前的缓存,直接传入 true 即可:

customer.orders                 # 读数据库
customer.orders.size            # 直接读缓存
customer.orders(true).empty?    # 不读缓存,重新查数据库

1. 避免命名冲突

       和几乎所有的语言告诉你的一样:”你可以随意命名,但是不要使用如下命名,因为他们会和我们的内置方法(变量)冲突“,所以,你不要使用可能与 ActiveRecord 重复的命名,比如:attributes 等。

       更新 schema这里的schema指的是数据库层面的 schema,一个比较官方的翻译是:模式,虽然 rails 的association已经非常智能,但是它不是万能的,你仍然需要在 migration 文件里手动的去创建外键字段等。关于 rails 的 migration,将会在接下来的分享中向大家分享,在这里有一个比较重要的点,那就是多对多中间表。

       当你创建了 has_and_belongs_to_many 的关系,你就需要创建一个中间表(如果觉得有些难懂,请看上一篇博文),如果你没有在创建关系时使用 join_table 参数,ActiveRecord 会按照词法顺序来命名,所以我们的例子中: customer 和 order 的中间表会是:customers_orders,因为字母 C 在字母 O 的前面。如果你想知道一些复杂的命名,或者不清楚词法顺序,可以在console里尝试一下比较,因为这个规则和 string <> 是一样的。但是要记住,无论你怎么命名,你都必须在 migration 里手动创建中间表:

class Developer < ActiveRecord::Base
  has_and_belongs_to_many :projects
end

class Project < ActiveRecord::Base
  has_and_belongs_to_many :developers
end

创建中间表的 migration 应该是:

class CreateDeveloperProjectJoinTable < ActiveRecord::Migration
  def change
    create_table :developers_projects, :id => false do |t|
      t.integer :developer_id
      t.integer :project_id
    end
  end
end

       要注意上面的那个 :id => false,中间表不代表任何模型,所以不要给它指定主键,否则你可能会遇到一些莫名其妙的错误。3. 关联关系作用域 说到作用域,就要涉及到元编程的一些内容,我们在这里不会详细去讨论那些诸如变量作用域之类的知识,先开门见山:rails 中关联关系的作用域是在一个 module 里。

module MyApplication
  module Business
    class Supplier < ActiveRecord::Base
       has_one :account
    end

    class Account < ActiveRecord::Base
       belongs_to :supplier
    end
  end
end

上面的代码会正常工作,大家注意 module, Supplier 和 Account 是在一个作用域里的。再看这个:

module MyApplication
  module Business
    class Supplier < ActiveRecord::Base
       has_one :account
    end
  end

  module Billing
    class Account < ActiveRecord::Base
       belongs_to :supplier
    end
  end
end

       它就不会正常工作,因为 Supplier 类和 Billing 类是在两个作用域里。如果想让上面的代码正常工作,你必须指定完整的 ClassName。

module MyApplication
  module Business
    class Supplier < ActiveRecord::Base
       has_one :account,
        :class_name => "MyApplication::Billing::Account"
    end
  end

  module Billing
    class Account < ActiveRecord::Base
       belongs_to :supplier,
        :class_name => "MyApplication::Business::Supplier"
    end
  end
end 

2. 双向关系

       Rails 中到处都是双向关系,比如一个 has_many 和一个 belongs_to 就可以称为一个双向关系。rails 默认是不知道这种关联的,这会造成对一个对象的两份拷贝(不同步),术语有一些晦涩,我们来看一个例子:

c = Customer.first
o = c.orders.first
c.first_name == o.customer.first_name # => true
c.first_name = 'Manny'
c.first_name == o.customer.first_name # => false

       为什么下面的结果会是 false,因为变量 c 和 o.custemer 在内存中是同一个数据不同的”表现“,所以当一个改变时,另外一个不会改变,ActiveRecord 提供了 :inverse_of 参数来让 rails 知道这种关系,此问题即可解决:

class Customer < ActiveRecord::Base
  has_many :orders, :inverse_of => :customer
end

class Order < ActiveRecord::Base
  belongs_to :customer, :inverse_of => :orders
end

通过声明的 inverse_of,ActiveRecord 会仅加载 customer 对象的一份拷贝,避免了数据的前后矛盾。

c = Customer.first
o = c.orders.first
c.first_name == o.customer.first_name # => true
c.first_name = 'Manny'
c.first_name == o.customer.first_name # => true

关于 inverse_of,你应该知道如下的几个知识点:

  1. 不能与 :through 一起工作
  2. 不能与 :polymorphic 一起工作
  3. 不能与 :as 一起工作
  4. 对于 belongs_to 关系,has_many 的 inverse_of 是被忽略的

本次分享就到这里,如果你有什么建议或者意见,请联系作者

支付宝扫码赞助博主


评论(0)