<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,5 +1,7 @@
 *Edge*
 
+* Add :validate option to associations to enable/disable the automatic validation of associated models. Resolves #301. [Jan De Poorter]
+
 * PostgreSQL: use 'INSERT ... RETURNING id' for 8.2 and later.  [Jeremy Kemper]
 
 * Added SQL escaping for :limit and :offset in MySQL [Jonathan Wiess]</diff>
      <filename>activerecord/CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -690,6 +690,7 @@ module ActiveRecord
       #   association is a polymorphic +belongs_to+.
       # * &lt;tt&gt;:uniq&lt;/tt&gt; - If true, duplicates will be omitted from the collection. Useful in conjunction with &lt;tt&gt;:through&lt;/tt&gt;.
       # * &lt;tt&gt;:readonly&lt;/tt&gt; - If true, all the associated objects are readonly through the association.
+      # * &lt;tt&gt;:validate&lt;/tt&gt; - If false, don't validate the associated objects when saving the parent object. true by default.
       #
       # Option examples:
       #   has_many :comments, :order =&gt; &quot;posted_on&quot;
@@ -710,7 +711,7 @@ module ActiveRecord
 
         configure_dependency_for_has_many(reflection)
 
-        add_multiple_associated_save_callbacks(reflection.name)
+        add_multiple_associated_save_callbacks(reflection.name) unless options[:validate] == false
         add_association_callbacks(reflection.name, reflection.options)
 
         if options[:through]
@@ -769,6 +770,7 @@ module ActiveRecord
       # * &lt;tt&gt;:source_type&lt;/tt&gt; - Specifies type of the source association used by &lt;tt&gt;has_one :through&lt;/tt&gt; queries where the source
       #   association is a polymorphic +belongs_to+.      
       # * &lt;tt&gt;:readonly&lt;/tt&gt; - If true, the associated object is readonly through the association.
+      # * &lt;tt&gt;:validate&lt;/tt&gt; - If false, don't validate the associated object when saving the parent object. +false+ by default.
       #
       # Option examples:
       #   has_one :credit_card, :dependent =&gt; :destroy  # destroys the associated credit card
@@ -799,7 +801,7 @@ module ActiveRecord
           end
           after_save method_name
 
-          add_single_associated_save_callbacks(reflection.name) 
+          add_single_associated_save_callbacks(reflection.name) if options[:validate] == true
           association_accessor_methods(reflection, HasOneAssociation)
           association_constructor_method(:build,  reflection, HasOneAssociation)
           association_constructor_method(:create, reflection, HasOneAssociation)
@@ -857,6 +859,7 @@ module ActiveRecord
       #   Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
       #   to the +attr_readonly+ list in the associated classes (e.g. &lt;tt&gt;class Post; attr_readonly :comments_count; end&lt;/tt&gt;).
       # * &lt;tt&gt;:readonly&lt;/tt&gt; - If true, the associated object is readonly through the association.
+      # * &lt;tt&gt;:validate&lt;/tt&gt; - If false, don't validate the associated objects when saving the parent object. +true+ by default.
       #
       # Option examples:
       #   belongs_to :firm, :foreign_key =&gt; &quot;client_of&quot;
@@ -937,6 +940,8 @@ module ActiveRecord
           )
         end
 
+        add_single_associated_save_callbacks(reflection.name) unless options[:validate] == false
+
         configure_dependency_for_belongs_to(reflection)
       end
 
@@ -1025,6 +1030,7 @@ module ActiveRecord
       # * &lt;tt&gt;:select&lt;/tt&gt; - By default, this is &lt;tt&gt;*&lt;/tt&gt; as in &lt;tt&gt;SELECT * FROM&lt;/tt&gt;, but can be changed if, for example, you want to do a join
       #   but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
       # * &lt;tt&gt;:readonly&lt;/tt&gt; - If true, all the associated objects are readonly through the association.
+      # * &lt;tt&gt;:validate&lt;/tt&gt; - If false, don't validate the associated objects when saving the parent object. +true+ by default.
       #
       # Option examples:
       #   has_and_belongs_to_many :projects
@@ -1037,7 +1043,7 @@ module ActiveRecord
       def has_and_belongs_to_many(association_id, options = {}, &amp;extension)
         reflection = create_has_and_belongs_to_many_reflection(association_id, options, &amp;extension)
 
-        add_multiple_associated_save_callbacks(reflection.name)
+        add_multiple_associated_save_callbacks(reflection.name) unless options[:validate] == false
         collection_accessor_methods(reflection, HasAndBelongsToManyAssociation)
 
         # Don't use a before_destroy callback since users' before_destroy
@@ -1343,7 +1349,8 @@ module ActiveRecord
             :uniq,
             :finder_sql, :counter_sql,
             :before_add, :after_add, :before_remove, :after_remove,
-            :extend, :readonly
+            :extend, :readonly,
+            :validate
           )
 
           options[:extend] = create_extension_modules(association_id, extension, options[:extend])
@@ -1353,7 +1360,7 @@ module ActiveRecord
 
         def create_has_one_reflection(association_id, options)
           options.assert_valid_keys(
-            :class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :readonly
+            :class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :readonly, :validate
           )
 
           create_reflection(:has_one, association_id, options, self)
@@ -1361,7 +1368,7 @@ module ActiveRecord
         
         def create_has_one_through_reflection(association_id, options)
           options.assert_valid_keys(
-            :class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :through, :source, :source_type
+            :class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :through, :source, :source_type, :validate
           )
           create_reflection(:has_one, association_id, options, self)
         end
@@ -1369,7 +1376,7 @@ module ActiveRecord
         def create_belongs_to_reflection(association_id, options)
           options.assert_valid_keys(
             :class_name, :foreign_key, :foreign_type, :remote, :select, :conditions, :include, :dependent,
-            :counter_cache, :extend, :polymorphic, :readonly
+            :counter_cache, :extend, :polymorphic, :readonly, :validate
           )
 
           reflection = create_reflection(:belongs_to, association_id, options, self)
@@ -1388,7 +1395,8 @@ module ActiveRecord
             :uniq,
             :finder_sql, :delete_sql, :insert_sql,
             :before_add, :after_add, :before_remove, :after_remove,
-            :extend, :readonly
+            :extend, :readonly,
+            :validate
           )
 
           options[:extend] = create_extension_modules(association_id, extension, options[:extend])</diff>
      <filename>activerecord/lib/active_record/associations.rb</filename>
    </modified>
    <modified>
      <diff>@@ -409,4 +409,23 @@ class BelongsToAssociationsTest &lt; ActiveRecord::TestCase
     sponsor.sponsorable = new_member
     assert_equal nil, sponsor.sponsorable_id
   end
+
+  def test_save_fails_for_invalid_belongs_to
+    assert log = AuditLog.create(:developer_id=&gt;0,:message=&gt;&quot;&quot;)
+
+    log.developer = Developer.new
+    assert !log.developer.valid?
+    assert !log.valid?
+    assert !log.save
+    assert_equal &quot;is invalid&quot;, log.errors.on(&quot;developer&quot;)
+  end
+
+  def test_save_succeeds_for_invalid_belongs_to_with_validate_false
+    assert log = AuditLog.create(:developer_id=&gt;0,:message=&gt;&quot;&quot;)
+
+    log.unvalidated_developer = Developer.new
+    assert !log.unvalidated_developer.valid?
+    assert log.valid?
+    assert log.save
+  end
 end</diff>
      <filename>activerecord/test/cases/associations/belongs_to_associations_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -342,6 +342,17 @@ class HasManyAssociationsTest &lt; ActiveRecord::TestCase
     assert new_firm.new_record?
   end
 
+  def test_invalid_adding_with_validate_false
+    firm = Firm.find(:first)
+    client = Client.new
+    firm.unvalidated_clients_of_firm &lt;&lt; Client.new
+
+    assert firm.valid?
+    assert !client.valid?
+    assert firm.save
+    assert client.new_record?
+  end
+
   def test_build
     company = companies(:first_firm)
     new_client = assert_no_queries { company.clients_of_firm.build(&quot;name&quot; =&gt; &quot;Another Client&quot;) }</diff>
      <filename>activerecord/test/cases/associations/has_many_associations_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -275,6 +275,18 @@ class HasOneAssociationsTest &lt; ActiveRecord::TestCase
     assert_equal &quot;is invalid&quot;, firm.errors.on(&quot;account&quot;)
   end
 
+
+  def test_save_succeeds_for_invalid_has_one_with_validate_false
+    firm = Firm.find(:first)
+    assert firm.valid?
+
+    firm.unvalidated_account = Account.new
+
+    assert !firm.unvalidated_account.valid?
+    assert firm.valid?
+    assert firm.save
+  end
+
   def test_assignment_before_either_saved
     firm = Firm.new(&quot;name&quot; =&gt; &quot;GlobalMegaCorp&quot;)
     firm.account = a = Account.new(&quot;credit_limit&quot; =&gt; 1000)</diff>
      <filename>activerecord/test/cases/associations/has_one_associations_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -160,9 +160,9 @@ class ReflectionTest &lt; ActiveRecord::TestCase
 
   def test_reflection_of_all_associations
     # FIXME these assertions bust a lot
-    assert_equal 20, Firm.reflect_on_all_associations.size
-    assert_equal 16, Firm.reflect_on_all_associations(:has_many).size
-    assert_equal 4, Firm.reflect_on_all_associations(:has_one).size
+    assert_equal 22, Firm.reflect_on_all_associations.size
+    assert_equal 17, Firm.reflect_on_all_associations(:has_many).size
+    assert_equal 5, Firm.reflect_on_all_associations(:has_one).size
     assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
   end
 </diff>
      <filename>activerecord/test/cases/reflection_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -26,6 +26,7 @@ class Firm &lt; Company
       &quot;AND (#{QUOTED_TYPE} = 'Client' OR #{QUOTED_TYPE} = 'SpecialClient' OR #{QUOTED_TYPE} = 'VerySpecialClient' )&quot;
   has_many :clients_sorted_desc, :class_name =&gt; &quot;Client&quot;, :order =&gt; &quot;id DESC&quot;
   has_many :clients_of_firm, :foreign_key =&gt; &quot;client_of&quot;, :class_name =&gt; &quot;Client&quot;, :order =&gt; &quot;id&quot;
+  has_many :unvalidated_clients_of_firm, :foreign_key =&gt; &quot;client_of&quot;, :class_name =&gt; &quot;Client&quot;, :validate =&gt; false
   has_many :dependent_clients_of_firm, :foreign_key =&gt; &quot;client_of&quot;, :class_name =&gt; &quot;Client&quot;, :order =&gt; &quot;id&quot;, :dependent =&gt; :destroy
   has_many :exclusively_dependent_clients_of_firm, :foreign_key =&gt; &quot;client_of&quot;, :class_name =&gt; &quot;Client&quot;, :order =&gt; &quot;id&quot;, :dependent =&gt; :delete_all
   has_many :limited_clients, :class_name =&gt; &quot;Client&quot;, :order =&gt; &quot;id&quot;, :limit =&gt; 1
@@ -46,7 +47,8 @@ class Firm &lt; Company
   has_many :plain_clients, :class_name =&gt; 'Client'
   has_many :readonly_clients, :class_name =&gt; 'Client', :readonly =&gt; true
 
-  has_one :account, :foreign_key =&gt; &quot;firm_id&quot;, :dependent =&gt; :destroy
+  has_one :account, :foreign_key =&gt; &quot;firm_id&quot;, :dependent =&gt; :destroy, :validate =&gt; true
+  has_one :unvalidated_account, :foreign_key =&gt; &quot;firm_id&quot;, :class_name =&gt; 'Account', :validate =&gt; false
   has_one :account_with_select, :foreign_key =&gt; &quot;firm_id&quot;, :select =&gt; &quot;id, firm_id&quot;, :class_name=&gt;'Account'
   has_one :readonly_account, :foreign_key =&gt; &quot;firm_id&quot;, :class_name =&gt; &quot;Account&quot;, :readonly =&gt; true
 end</diff>
      <filename>activerecord/test/models/company.rb</filename>
    </modified>
    <modified>
      <diff>@@ -57,6 +57,7 @@ end
 
 class AuditLog &lt; ActiveRecord::Base
   belongs_to :developer
+  belongs_to :unvalidated_developer, :class_name =&gt; 'Developer', :validate =&gt; false
 end
 
 DeveloperSalary = Struct.new(:amount)</diff>
      <filename>activerecord/test/models/developer.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>f728e57d2204a429f5282856ec89d4e047e72957</id>
    </parent>
  </parents>
  <author>
    <name>Jan De Poorter</name>
    <login>DefV</login>
    <email>jan@defv.be</email>
  </author>
  <url>http://github.com/rails/rails/commit/7f140bbddaf70abc61570f6cfdcbfba5771ffc78</url>
  <id>7f140bbddaf70abc61570f6cfdcbfba5771ffc78</id>
  <committed-date>2008-06-11T04:21:57-07:00</committed-date>
  <authored-date>2008-06-11T04:08:35-07:00</authored-date>
  <message>Add :validate option to associations. [#301 state:resolved]

Signed-off-by: Pratik Naik &lt;pratiknaik@gmail.com&gt;</message>
  <tree>6c3622bd7951295b58ec5c5bbfceeb7ffb6def21</tree>
  <committer>
    <name>Pratik Naik</name>
    <login>lifo</login>
    <email>pratiknaik@gmail.com</email>
  </committer>
</commit>
