This project is archived and is in readonly mode.
Inconsistent has_many validation.
-
Eloy Duran
Just to clarify a bit. Judging by the tag you are sure this was introduced in the first 2.3 release candidate?
-
Keith Pitt
I added that tag because thats the version I'm using (I thought that might be useful). But I haven't checked to see if its in the current stable version of rails.
-
Eloy Duran
- Tag changed from 2.3-rc1, activerecord to activerecord
It would be very helpful if you could identify where (if) this was introduced.
Thanks
-
Keith Pitt
Whats the best (fastest) way for me to do that?
-
Eloy Duran
However, if I do something like this:
@@@ ruby a = ModelA.create a.model_bs << ModelB.create a.valid? # => true a.model_bs.first.name = nil a.valid? # => true @@@
the parent record is still valid even though I make an existing child record invalid.
Hmm, actually this might be because the target on the existing parent isn't loaded when using #push (See http://is.gd/mgf4).
>If the target isn't loaded yet and you then use #first, it will not load the target. So that specific object you get returned is not going to be found by autosave association when iterating over any loaded records.
You should be able to verify this by:
- Not creating a parent object, so it's still new when #<< is called and the target will be loaded.
- First load the target before using #first, because then #first will simply return from the loaded target. You can use this as a trick to make sure it's loaded: a.model_bs.class
If it is this problem, then there's unfortunately no easier solution. However, I do found that using #first is something which is often only done in tests or script/console. Is that the case?
I actually created a patch for this once, but it won't get applied in that state which I agree with. See #1751 AssociationCollection #first and #last cache.
-
Eloy Duran
Jeez, formatting is a pita. I edited it a few times, hope you can understand it.
-
Eloy Duran
Whats the best (fastest) way for me to do that?
The fastest way to find where a bug was introduced, if it was, would be to use git-bisect. You create a file with a failing test and then do the whole git-bisect workflow and run the test each time to see where it/if it was introduced.
-
Eloy Duran
Oh and I forgot to add that it is weird that validates_associated would fix this…
-
José Valim
This is very strange, because by default has_many validation was supposed to have :validate => true.
Can you please try:
has_many :model_bs, :validate => true
And without the validates_associated?
If you can still see a failure scenario, this is a serious bug, since validates_associated is behaving in a different way then :validate => true.
-
Keith Pitt
has_many :model_bs, :validate => true
Tried that - but it still fails.
I tried this, as per your thoughts Eloy ( hopefully I understood them correctly, your saying the problem is the parent not being loaded by just doing a #first? )
a = ModelA.create a.model_bs << ModelB.create a.valid? # => true x = a.model_bs.first y = x.model_a x.name = nil y.valid? # => trueBut alas, still doesn't work as expected.
-
Keith Pitt
Here is my test script. I tested it with rails 2.2 (stable) and my tests fail. I went back to 2.1 but got more errors (not to do with me tests) - do I need to go back any further?
-
Eloy Duran
- Assigned user set to Eloy Duran
- State changed from new to invalid
I tested it with rails 2.2 (stable) and my tests fail. I went back to 2.1 but got more errors (not to do with me tests) - do I need to go back any further?
That won't be necessary. Today I had some time to play with this (thanks for your test example) and it's pretty clear why this doesn't work. In the case of a collection association (such as a has_many) on a not new parent, only new associated records will be validated. (Also the default for a collection association is :validate => true, so setting that won't matter either.)
So to answer your very first question: It's not a bug, it's a feature.
The examples in which you expect that clearing the name of an existing Post will make the parent invalid won't work, as it simply skips that associated record. And trying to add new records with #<< won't work either, as they're automatically saved. (Use #build for that.)
If you do want to always validate all associated records, you could enable :autosave. That way any loaded record will always get validated. However, they will also get saved together with the parent, but that might not be a problem in your situation. Keep in mind though that the problem about using #first still applies.
-
Eloy Duran
I tried this, as per your thoughts Eloy ( hopefully I understood them correctly, your saying the problem is the parent not being loaded by just doing a #first? )
You didn't completely got my earlier comment, which is understandable as it can be strange/unexpected. What I mean is that you can't use #first or #last in this way unless the ‘target’ was loaded first by calling, for instance, #each on the collection. See #1752 Modifying with_connection method for more info. But it won't matter in your case.
