Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #8841 (closed defect: invalid)

Opened 3 years ago

Last modified 2 years ago

[PATCH] Polymorphic has_one and has_many should set proper polymorphic_type in case of STI

Reported by: lifofifo Assigned to: core
Priority: normal Milestone: 1.x
Component: ActiveRecord Version: edge
Severity: normal Keywords:
Cc: tarmo

Description

Right now, it always sets the polymorphic_type as that of base class in case of STI is used. The worse part is, even the tests cases check for the wrong type.

This patch fixes the issue and also correct the test case, in addition to adding new test cases.

Attachments

fix_poly_type_for_sti.patch (2.0 kB) - added by lifofifo on 07/02/07 17:27:22.
Set proper type field for STI models with polymorphic association defined in parent

Change History

07/02/07 17:27:22 changed by lifofifo

  • attachment fix_poly_type_for_sti.patch added.

Set proper type field for STI models with polymorphic association defined in parent

07/02/07 17:29:59 changed by lifofifo

In existing test case, it uses posts(:thinking) which has type "SpecialPost" defined in fixture, but test checks for type "Post".

07/02/07 18:18:46 changed by technoweenie

  • status changed from new to closed.
  • resolution set to invalid.

This is expected behavior. There should be no problems with checking for the base class.

07/02/07 21:47:13 changed by lifofifo

Just wanted to point out a valid usecase where this could make a difference.

Thanks, Pratik

07/02/07 23:29:29 changed by tarmo

I can't see why this should be the "expected behaviour". Ofcourse the record can be found through either but why not use the name that more closely identifies what is being looked for?

(follow-up: ↓ 6 ) 07/02/07 23:29:39 changed by tarmo

  • cc set to tarmo.

(in reply to: ↑ 5 ; follow-up: ↓ 9 ) 07/03/07 03:49:32 changed by hasmanyjosh

Replying to tarmo:

The purpose of the polymorphic_type field is to identify the table the polymorphic_id foreign key points into. If that table is an STI table, then you should use the base class for that table as the polymorphic_type. If someone mutated a record in that table to be a different class (difficult but not impossible to pull off), then using the concrete class as the poly type would screw you up bigtime. There are probably other implications too, but that's enough for me now.

07/03/07 03:58:20 changed by tarmo

Ok, I can agree with this.

As for lifofifo use case, why not validate against the actual attachment.attachable.class? Yes it would create an additional SQL request if attachable is not yet loaded but it's not that bad, is it?

12/17/07 17:32:02 changed by basic

The correct type must be stored, or destroy() may fail. It did that in Rails 1.2, even though a loop through the children worked fine. Is this fixed in Rails 2?

http://pastie.caboo.se/129651

(in reply to: ↑ 6 ) 03/25/08 07:32:14 changed by Danimal

Replying to hasmanyjosh:

Replying to tarmo: The purpose of the polymorphic_type field is to identify the table the polymorphic_id foreign key points into. If that table is an STI table, then you should use the base class for that table as the polymorphic_type. If someone mutated a record in that table to be a different class (difficult but not impossible to pull off), then using the concrete class as the poly type would screw you up bigtime. There are probably other implications too, but that's enough for me now.

But what about the case where the base class is abstract?

I've been banging my head against this for hours. I finally applied the patches from #6485 and everything works great. Here's my example, a case where there is an abstract, base class for the poly:

http://pastie.caboo.se/170166

Before the patch, calls to Buyer.find(:first).users would return an empty array because it was building it based upon the base class: Account instead of the actual class as stored in the users table.

(and actually, I had to add: :dependent => :destroy to the Account class to get it to work right: Buyer.find(:first).destroy => destroys the buyer account plus all user accounts associated with that buyer via the account_id and account_type fields)