经验:声明中有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 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.