经验:声明中有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 a has_and_belongs_to_many relationship (t hough 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
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.