Skip to content


ActiveRecord builds instance of wrong class through a scope targeting an STI class #7021

steveluscher opened this Issue · 8 comments

3 participants


I would like to be able to call the build method on a scope that targets a certain class of model via its STI type, and have ActiveRecord build an instance of the correct class.

class Post < ActiveRecord::Base
  scope :special, where(type: 'SpecialPost')

class SpecialPost < Post; end

> # Expect an instance of SpecialPost here
=> #<Post ...>

Here, I expected an instance of SpecialPost, not an instance of Post.

Ruby on Rails member

This does not work, and I believe it's right the way it is. Setting the type attribute in a scope doesn't mean Active Record will build that particular type object when using STI, it only means it'll set that type attribute when building your post.

So, doing should give you a Post with a SpecialPost type, nothing more. If you save and reload, you'd probably get a SpecialPost instead.. And if you want a new instance of SpecialPost, you'd have to do instead.



I respectfully disagree. Using the exact same code above:

> Post.special.first
=> #<SpecialPost ...>
=> #<Post ...>

Surely we can't advocate for a design where the association getter does one thing, and the association builder does another.

Ruby on Rails member

special is an association or a scope? if it is a scope this behavior is right.


You are correct that I misspoke. I meant to say “scope getter does one thing, and the scope builder does another.”

My claim stands: for Model.scope.first to return an instance of one class and to return an instance of a different class breaks my expectation of what that scope represents.

Ruby on Rails member

A scope can't know anything about STI. Scopes always return the caller class instance when you are using the build method, this is expected behavior.



So, are we sure that we don't want this to work?

class Citizen
  scope :canadian, where(type: 'CanadianPerson')
  scope :american, where(type: 'AmericanPerson')
  scope :swedish,  where(type: 'SwedishPerson')

nationality = [:canadian, :american, :swedish, ...]

p = Citizen(nationality.sample).build
=> #<CanadianCitizen ...>
Ruby on Rails member

We can make this work. But right now it is not an issue. You can either open a pull request or discuss in the Rails Core mailing list.


Rails 4.0 seems to have rolled this in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.