现在的位置: 首页 > 综合 > 正文

rails active record associations

2014年03月26日 ⁄ 综合 ⁄ 共 8512字 ⁄ 字号 评论关闭

经验:声明中有has的model在保存的时候,另一方默认自动保存

The has_many :through Association

class Physician < ActiveRecord::Base
  has_many :appointments
  has_many :patients, :through => :appointments
end
 
class Appointment < ActiveRecord::Base
  belongs_to :physician
  belongs_to :patient
end
 
class Patient < ActiveRecord::Base
  has_many :appointments
  has_many :physicians, :through => :appointments
end

Automatic deletion of join models is direct, no destroy callbacks are triggered.

The has_one :through Association

class Supplier < ActiveRecord::Base
  has_one :account
  has_one :account_history, :through => :account
end
 
class Account < ActiveRecord::Base
  belongs_to :supplier
  has_one :account_history
end
 
class AccountHistory < ActiveRecord::Base
  belongs_to :account
end

Choosing Betweenhas_many :through andhas_and_belongs_to_many

If you don’t need to do anything with the relationship model, it may be simpler to set up ahas_and_belongs_to_many relationship (though you’ll need to remember to create the joining table in the database).

You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.

Polymorphic Associations

With polymorphic associations, a model can belong to more than one other model, on a single association.

class Picture < ActiveRecord::Base
  belongs_to :imageable, :polymorphic => true
end
 
class Employee < ActiveRecord::Base
  has_many :pictures, :as => :imageable
end
 
class Product < ActiveRecord::Base
  has_many :pictures, :as => :imageable
end

If you have an instance of the Picture model, you can get to its parent via@picture.imageable. To make this work, you need to declare both a foreign key column and a type column in the model that declares the polymorphic interface:

class CreatePictures < ActiveRecord::Migration
  def self.up
    create_table :pictures do |t|
      t.string  :name
      t.integer :imageable_id
      t.string  :imageable_type
      t.timestamps
    end
  end
 
  def self.down
    drop_table :pictures
  end
end

This migration can be simplified by using the t.references form:

class CreatePictures < ActiveRecord::Migration
  def self.up
    create_table :pictures do |t|
      t.string :name
      t.references :imageable, :polymorphic => true
      t.timestamps
    end
  end
 
  def self.down
    drop_table :pictures
  end
end

Self Joins

class Employee < ActiveRecord::Base
  has_many :subordinates, :class_name => "Employee",
    :foreign_key => "manager_id"
  belongs_to :manager, :class_name => "Employee"
end

With this setup, you can retrieve @employee.subordinates and @employee.manager.

Tips, Tricks, and Warnings

Controlling Caching

customer.orders                 # retrieves orders from the database
customer.orders.size            # uses the cached copy of orders
customer.orders(true).empty?    # discards the cached copy of orders
                                # and goes back to the database

Updating the Schema

You are responsible for maintaining your database schema to match your associations. In practice, this means two things, depending on what sort of associations
you are creating. For belongs_to associations
you need to create foreign keys, and for has_and_belongs_to_many associations
you need to create the appropriate join table.
class Order < ActiveRecord::Base
  belongs_to :customer
end

class CreateOrders < ActiveRecord::Migration
  def self.up
    create_table :orders do |t|
      t.datetime :order_date
      t.string   :order_number
      t.integer  :customer_id
    end
  end
 
  def self.down
    drop_table :orders
  end
end

If you create an association some time after you build the underlying model, you need to remember to create an add_column migration
to provide the necessary foreign key.


 Creating Join Tables for has_and_belongs_to_many Associations

class CreateAssemblyPartJoinTable < ActiveRecord::Migration
  def self.up
    create_table :assemblies_parts, :id => false do |t|
      t.integer :assembly_id
      t.integer :part_id
    end
  end
 
  def self.down
    drop_table :assemblies_parts
  end
end

 Controlling Association Scope

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

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

Detailed Association Reference

belongs_to Association Reference

Methods Added by belongs_to
    association(force_reload = false)
    association=(associate)
    build_association(attributes = {})
    create_association(attributes = {})

In all of these methods, association is replaced with the symbol passed as the first argument to
belongs_to.

Options for belongs_to

The belongs_to association supports these options:

    :autosave
    :class_name
    :conditions
    :counter_cache
    :dependent
    :foreign_key
    :include
    :polymorphic
    :readonly
    :select
    :touch
    :validate

Although the :counter_cache option is specified on the model that includes the
belongs_to declaration, the actual column must be added to the associated model. In the case above, you would need to add a column named
orders_count to the Customer model. You can override the default column name if you need to:

class Order < ActiveRecord::Base
  belongs_to :customer, :counter_cache => :count_of_orders
end
class Customer < ActiveRecord::Base
  has_many :orders
end

:dependent

You should not specify this option on a belongs_to association that is connected with a
has_many association on the other class. Doing so can lead to orphaned records in your database.

if @order.customer.nil?
  @msg = "No customer found for this order"
end

When are Objects Saved?

Assigning an object to a belongs_to association does not automatically save the object. It does not save the associated object either.

has_one Association Reference

If you set the :dependent option to :nullify, then deleting this object will set the foreign key in the association object to
NULL.

When you assign an object to a has_one association, that object is automatically saved (in order to update its foreign key). In addition, any object being replaced is also automatically saved, because its foreign key will change too.

has_many Association Reference

Methods Added by has_many
    collection(force_reload = false)
    collection<<(object, …)
    collection.delete(object, …)
    collection=objects
    collection_singular_ids
    collection_singular_ids=ids
    collection.clear
    collection.empty?
    collection.size
    collection.find(…)
    collection.exists?(…)
    collection.build(attributes = {}, …)
    collection.create(attributes = {})

The has_many association supports these options:

    :as
    :autosave
    :class_name
    :conditions
    :counter_sql
    :dependent
    :extend
    :finder_sql
    :foreign_key
    :group
    :include
    :limit
    :offset
    :order
    :primary_key
    :readonly
    :select
    :source
    :source_type
    :through
    :uniq
    :validate
class Customer < ActiveRecord::Base
  has_many :confirmed_orders, :class_name => "Order",
    :conditions => { :confirmed => true }
end

If you use a hash-style :conditions option, then record creation via this association will be automatically scoped using the hash. In this case, using
@customer.confirmed_orders.create or @customer.confirmed_orders.build will create orders where the confirmed column has the value
true.

has_and_belongs_to_many Association Reference

    collection(force_reload = false)
    collection<<(object, …)
    collection.delete(object, …)
    collection=objects
    collection_singular_ids
    collection_singular_ids=ids
    collection.clear
    collection.empty?
    collection.size
    collection.find(…)
    collection.exists?(…)
    collection.build(attributes = {})
    collection.create(attributes = {})
    :association_foreign_key
    :autosave
    :class_name
    :conditions
    :counter_sql
    :delete_sql
    :extend
    :finder_sql
    :foreign_key
    :group
    :include
    :insert_sql
    :join_table
    :limit
    :offset
    :order
    :readonly
    :select
    :uniq
    :validate
class User < ActiveRecord::Base
  has_and_belongs_to_many :friends, :class_name => "User",
    :foreign_key => "this_user_id",
    :association_foreign_key => "other_user_id"
end

:insert_sql

Normally Rails automatically generates the proper SQL to create links between the associated classes. With the
:insert_sql option, you can specify a complete SQL statement to insert them yourself.

:delete_sql

Normally Rails automatically generates the proper SQL to remove links between the associated classes. With the
:delete_sql option, you can specify a complete SQL statement to delete them yourself.

:counter_sql

If you specify :finder_sql but not :counter_sql, then the counter
SQL will be generated by substituting SELECT COUNT(*) FROM for the
SELECT ... FROM clause of your :finder_sql statement.

Association Callbacks

Association callbacks are similar to normal callbacks, but they are triggered by events in the life cycle of a collection. There are four available association callbacks:

    before_add
    after_add
    before_remove
    after_remove

class Customer < ActiveRecord::Base
  has_many :orders,
    :before_add => [:check_credit_limit, :calculate_shipping_charges]
 
  def check_credit_limit(order)
    ...
  end
 
  def calculate_shipping_charges(order)
    ...
  end
end

Association Extensions

module FindRecentExtension
  def find_recent
    find(:all, :conditions => ["created_at > ?", 5.days.ago])
  end
end
 
class Customer < ActiveRecord::Base
  has_many :orders, :extend => FindRecentExtension
end
 
class Supplier < ActiveRecord::Base
  has_many :deliveries, :extend => FindRecentExtension
end

Extensions can refer to the internals of the association proxy using these three accessors:

  • proxy_owner returns the object that the association is a part of.
  • proxy_reflection returns the reflection object that describes the association.
  • proxy_target returns the associated object for belongs_to or
    has_one, or the collection of associated objects for has_many or
    has_and_belongs_to_many.

抱歉!评论已关闭.