#1042 ✓resolved
Ashley Moran

STI :child_key option causes DataMapper to try to save both keys

Reported by Ashley Moran | September 15th, 2009 @ 11:46 AM | in 1.0.0

This also appears to be a 0.10RC2 regression. I'm pretty sure we've seen the same error on 0.10RC1, which means we may have done something to work around it.

This is the SQL that gets generated:

INSERT INTO "hats" ("type", "person_id", "employee_id") VALUES ('felt', 1, 1)

but it should be:

INSERT INTO "hats" ("type", "person_id") VALUES ('felt', 1)

Failing spec:

require 'rubygems'

DM_GEMS_VERSION   = "0.10.0"
DO_GEMS_VERSION   = "0.10.0"
 
gem "data_objects",   DO_GEMS_VERSION
gem "do_sqlite3",     DO_GEMS_VERSION
gem "dm-core",        DM_GEMS_VERSION   
gem "dm-types",       DM_GEMS_VERSION        
gem "dm-validations", DM_GEMS_VERSION  

require "data_objects"
require "dm-core"
require "dm-types"
require "dm-validations"

require 'spec'
 
DataMapper.setup(:default, "sqlite3::memory:")

class Hat
  include DataMapper::Resource
  
  property :id, Serial
  property :type, String
  
  belongs_to :employee, :child_key => [ :person_id ]
end

class Person
  include DataMapper::Resource

  property :id, Serial
  property :type, Discriminator
end

class Employee < Person
  has 1, :hat
  
  def initialize(options = {})
    self.hat = Hat.new(:type => "felt")
    super
  end
end
 
Spec::Runner.configure do |config|
  config.before(:each) do
    Hat.auto_migrate!
    Person.auto_migrate!
  end
end

describe "STI subclass can't construct" do
  it "tries to save both keys, the one specified in :child_key and an 'implicit' one from the STI base class" do
    @employee = Employee.new
    lambda { @employee.save }.should_not raise_error
  end
end

Comments and changes to this ticket

  • Jonathan Stott (namelessjon)

    Jonathan Stott (namelessjon) October 16th, 2009 @ 12:12 PM

    • Tag set to 0.10.1, relationships, sti
    • State changed from “new” to “confirmed”
  • Dan Kubb (dkubb)

    Dan Kubb (dkubb) March 10th, 2010 @ 03:31 AM

    • State changed from “confirmed” to “resolved”
    • Assigned user set to “Dan Kubb (dkubb)”
    • Milestone set to 1.0.0

    In the the case in Ashley's comment above, there are relationships on both sides pointing to the other model, but because they aren't named using the naming convention, there's no way for DM to know they should be made inverses of each other. Specifying the :child_key explicitly is the only sane way I can think of to make this work. If there are alternative ideas, I would be open to hear them.

    The original case can also be explained:

    The algorithm for identifying inverse relationships is relatively simple: it looks at the target model and tries to find a relationship with the expected name, a target model equal to it's source and a parent_key/child_key equal to it's own. In this case "has 1, :hat" wouldn't match the "employee" relationship in Hat because the parent_key is different from what it'd set up by default.

    Changing both relationships to use the same :parent_key, or removing the :parent_key altogether results in the attached specs passing with edge dm-core (not tested with other dm-core's in the 0.10 series, but I would expect all of them to work the same in this regard)

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »