Forum: Ruby on Rails Built associations don't know their parent. Why not?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
hakunin (Guest)
on 2009-04-22 20:07
(Received via mailing list)
Simple example.

class Car < ActiveRecord::Base
  has_one :engine

  validates_presence_of :engine
  validates_associated :engine
end

class Engine < ActiveRecord::Base
  belongs_to :car

  validates_presence_of :car
end

@car = Car.new
@car.valid? # => false
@car.errors # => no engine obviously

@car.build_engine
@car.valid? # => false

How come??

@car.errors # => engine has no car!
@car.engine.car # => nil

Since we validate_associated :engine, engine runs its own validations
and finds that  validates_presence_of car is false, since car is nil.
Shouldn't engine know the car that built it?

Just for fun I do this:

@car.engine.car = @car
@car.valid? # => true

Shouldn't this work by default?
hakunin (Guest)
on 2009-04-22 20:21
(Received via mailing list)
If you have suggestions, I moved this to Core:
http://groups.google.com/group/rubyonrails-core/br...
Eloy D. (Guest)
on 2009-04-23 15:32
(Received via mailing list)
I agree. Please write a patch :)

Eloy
Flinn Mueller (Guest)
on 2009-04-23 15:56
(Received via mailing list)
Shouldn't you be validating presence of the key rather than the object
itself?
hakunin (Guest)
on 2009-04-23 16:30
(Received via mailing list)
Eloy, I'll give it a shot. : )

Flinn, not necessarily. In this case we talk about associated in-
memory object which doesn't have id yet. In my opinion
validates_presence_of :engine_id should behave the old way - check for
id - since we explicitly ask for it.
Eloy D. (Guest)
on 2009-04-23 16:30
(Received via mailing list)
A key wouldn't be possible because you'd need to save a possibly
invalid record first to be able to do that.
Let me note that I don't necessarily have a need for
validate_presence_of to work in this case, but rather the ability of
having the parent object around when doing stuff in the child model.

Eloy
hakunin (Guest)
on 2009-04-23 16:33
(Received via mailing list)
Well, if the child cannot exist without parent, it seems natural to
have such validation in the child - must have parent. (In addition to
your reasoning.)
kscaldef (Guest)
on 2009-04-23 18:23
(Received via mailing list)
I had a closely related question today, which was why this code does N
+1 queries?

order.order_items.each {|oi| oi.order}

Why must each order item do a query to find its order?  It seems like
Rails should recognize the reciprocal nature of the associations.


-kevin
Michael K. (Guest)
on 2009-04-23 19:45
(Received via mailing list)
On Thu, Apr 23, 2009 at 9:16 AM, kscaldef <removed_email_address@domain.invalid>
wrote:
>
> I had a closely related question today, which was why this code does N
> +1 queries?
>
> order.order_items.each {|oi| oi.order}
>
> Why must each order item do a query to find its order?  It seems like
> Rails should recognize the reciprocal nature of the associations.

Yes,  this is called bi-directional associations and would be a
welcome (if non-trivial) addition in 3.0.

Incidentally it actually only does 1 query, the rest are cache hits,
so at least your database is safe.  However it still wastes memory and
can lead to some weirdness and should be fixed.

The has_many :foos, belongs_to :bar case is very easy, but there are
some common corner cases which we'll have to look at.  For example
what's the inverse of belongs_to :bar when we have the following
associations to choose from:

has_many :foos, :conditions=>...
has_many :something_else, :class_name=>"Foo"
has_many :important_foos, :through=>...

Ideally we'd just punt on those cases and require users to explicitly
mark associations as inverses with a declaration like

has_many :foos, :conditions ... :inverse_of=>:bar

--
Cheers

Koz
Jeremy E. (Guest)
on 2009-04-23 20:13
(Received via mailing list)
On Wed, Apr 22, 2009 at 3:42 PM, Michael K.
<removed_email_address@domain.invalid> wrote:
>
> associations to choose from:
>
> has_many :foos, :conditions=>...
> has_many :something_else, :class_name=>"Foo"
> has_many :important_foos, :through=>...
>
> Ideally we'd just punt on those cases and require users to explicitly
> mark associations as inverses with a declaration like
>
> has_many :foos, :conditions ... :inverse_of=>:bar

FWIW, that's basically how Sequel handles it, with the :reciprocal
option, though instead of punting by default if multiple associations
match, it picks the first matching association.

Jeremy
Michael K. (Guest)
on 2009-04-23 20:29
(Received via mailing list)
> FWIW, that's basically how Sequel handles it, with the :reciprocal
> option, though instead of punting by default if multiple associations
> match, it picks the first matching association.

The first matching association sounds like it could be prone to
surprises but we could see how it plays out.  Fundamentally though
having something that works with explicit declarations only would be a
good start.  How we mark the associations is kinda orthogonal to the
associations' behaviour which is the real challenge.


--
Cheers

Koz
Jonas N. (Guest)
on 2009-04-24 14:54
(Received via mailing list)
Wouldn't all of these problems be solved by an identity map? Wouldn't
that
be a better solution than trying to hack this on the existing system?

/Jonas
Murray S. (Guest)
on 2009-04-24 19:25
(Received via mailing list)
2009/4/22 Michael K. <removed_email_address@domain.invalid>

> > Rails should recognize the reciprocal nature of the associations.
> what's the inverse of belongs_to :bar when we have the following
>
I wrote this up as a patch a while ago, not enough folk looked at it
from
Rails 2.3, but maybe more people can look at it for 3.0? The patch is
here:
http://rails.lighthouseapp.com/projects/8994/ticke...

It's based on a plugin I wrote called parental_control :
http://github.com/hlame/parental_control

The patch demands a :inverse syntax, but the plugin uses "magic" to try
and
work it out correctly.

Cheers,

Muz
Murray S. (Guest)
on 2009-04-24 19:26
(Received via mailing list)
2009/4/24 Murray S. <removed_email_address@domain.invalid>

>> >
>> The has_many :foos, belongs_to :bar case is very easy, but there are
>>
>> has_many :foos, :conditions ... :inverse_of=>:bar
>>
>
> I wrote this up as a patch a while ago, not enough folk looked at it from
> Rails 2.3, but maybe more people can look at it for 3.0? The patch is here:
> 
http://rails.lighthouseapp.com/projects/8994/ticke...
>
> It's based on a plugin I wrote called parental_control :
> http://github.com/hlame/parental_control
>

Um ... that's http://github.com/h-lame/parental_control

I keep forgetting which apps allow / don't allow hyphens in usernames...
Michael K. (Guest)
on 2009-04-25 08:40
(Received via mailing list)
On Thu, Apr 23, 2009 at 6:24 PM, Jonas N. <removed_email_address@domain.invalid>
wrote:
> Wouldn't all of these problems be solved by an identity map? Wouldn't that
> be a better solution than trying to hack this on the existing system?

Yes, bi-directional associations are a localised implementation of an
identity map.  As has been discussed several times before, an identity
map would be a welcome addition to AR, but would be a *lot* of work
given the functionality we support (:select etc) and the strange ways
it can all interact.

Bi-Directional associations would give 99% of the return with much
less of the complication.

--
Cheers

Koz
Will B. (Guest)
on 2009-04-25 13:53
(Received via mailing list)
On Thu, Apr 23, 2009 at 6:24 PM, Jonas N. <removed_email_address@domain.invalid>
wrote:
> Wouldn't all of these problems be solved by an identity map? Wouldn't that
> be a better solution than trying to hack this on the existing system?

Correct me if I'm wrong, but an identity map does nothing to help with
unsaved records.  For example, if I build children on a parent record,
I should be able to have validates_presence_of :parent in the child
model - but can't.

Of course, this already works fine without an identity map in the
update case, where the parent ID has already been set and so the
record can be found, but create is the hard one.  So an identity map
doesn't really solve many problems, though there are potentially
efficiency gains.
cainlevy (Guest)
on 2009-04-25 19:48
(Received via mailing list)
I wrote a "reverse" association finder in ActiveScaffold a couple
years ago. Works pretty well, I think:

http://github.com/activescaffold/active_scaffold/b...

Also, I've grown very accustomed to writing bi-directional association
validations in my home-grown validation library. Granted, I've always
had to set the "reverse" association manually which looks a little
shoddy in my controllers, but the key to making it all work was:

http://github.com/cainlevy/semantic-attributes/blo...
This topic is locked and can not be replied to.