#939 confirmed
Ashley Moran

Assigning a parent to a child does not work the same as assigning a child to a parent

Reported by Ashley Moran | July 1st, 2009 @ 07:12 AM

This bug makes it hard to create complex object graphs because you have to be careful to create the objects by assigning children to parents. If you do it the other way round, the objects don't save, because the parent isn't aware it has a child to save. You can see this in the following example, which has one example passing and one failing:

require 'rubygems'
 
USE_DM_0_9 = false
 
if USE_DM_0_9
  DM_GEMS_VERSION   = "0.9.11"
  DO_GEMS_VERSION   = "0.9.12"
else
  DM_GEMS_VERSION   = "0.10.0"
  DO_GEMS_VERSION   = "0.10.0"
end
 
gem "data_objects",    DO_GEMS_VERSION
gem "do_postgres",     DO_GEMS_VERSION # If using another database, replace this
gem "dm-core",         DM_GEMS_VERSION         
gem "dm-aggregates",   DM_GEMS_VERSION   
gem "dm-migrations",   DM_GEMS_VERSION   
gem "dm-timestamps",   DM_GEMS_VERSION   
gem "dm-types",        DM_GEMS_VERSION        
gem "dm-validations",  DM_GEMS_VERSION  
gem "dm-serializer",   DM_GEMS_VERSION  
 
require "data_objects"
require "do_postgres"
require "dm-core"
require "dm-aggregates"
require "dm-migrations"
require "dm-timestamps"
require "dm-types"
require "dm-validations"
require "dm-serializer"

require 'spec'
 
# SQLITE_FILE = File.join(`pwd`.chomp, "test.db")
 
# DataMapper.setup(:default, "sqlite3:#{SQLITE_FILE}")
# DataMapper.setup(:reloaded, "sqlite3:#{SQLITE_FILE}")
 
DataMapper.setup(:default, "postgres://andy@localhost/datamapper_test")
DataMapper.setup(:reloaded, "postgres://andy@localhost/datamapper_test")
 
class Parent
  include DataMapper::Resource
  property :id, Serial
  property :name, String, :nullable => false
  
  validates_is_unique :name
  
  has n, :kids, :order => [:name]
  
  def ==(other)
    other.kids == kids && other.name == name
  end
  
  def the_same_when_you_do_the_comparison_the_other_way_round?(other)
    other.name == name && other.kids == kids
  end
end
 
class Kid
  include DataMapper::Resource
  property :id, Serial
  property :name, String
  
  belongs_to :parent, :model => "Parent", :child_key => [:parent_id]  
  
  validates_is_unique :name, :scope => :parent
end
 
module IdentityMapHelper
  def reload(object)
    object.class.get(object.id)
  end
  
  def with_db_reconnect(&blk)
    original_repository = DataMapper::Repository.context.pop
    repository(:reloaded, &blk)
    DataMapper::Repository.context << original_repository
  end
end
 
Spec::Runner.configure do |config|
  include IdentityMapHelper
  
  config.before(:each) do
    Parent.auto_migrate!
    Kid.auto_migrate!
  end
  
  config.before(:each) do    
    DataMapper::Repository.context << repository(:default)
  end
  
  config.after(:each) do
    DataMapper::Repository.context.pop
  end
end
 
describe Parent, "with kids, 1 to n" do
  it "should have let the parent know it's got a kid" do
    @parent = Parent.new(:name => "Andy")
    @kid = Kid.new(:name => "Bart", :parent => @parent)
    
    @kid.parent.should == @parent
    @parent.should have(1).kids
  end
  
  it "should let the kid know who it belongs to" do
    @kid = Kid.new(:name => "Bart")
    @parent = Parent.new(:name => "Andy", :kids => [@kid])

    @parent.should have(1).kids
    @kid.parent.should == @parent
  end
end

Comments and changes to this ticket

  • Dan Kubb (dkubb)

    Dan Kubb (dkubb) July 3rd, 2009 @ 02:55 AM

    • Assigned user set to “Dan Kubb (dkubb)”
    • State changed from “new” to “accepted”
  • Dan Kubb (dkubb)

    Dan Kubb (dkubb) July 3rd, 2009 @ 03:13 AM

    This ticket actually goes back quite a way to #488. I'm going to leave this here though because it has specs ;) (even though we actually have had pending specs for this in dm-core/next for 4 or 5 months)

    Now that alot of the Relationship#inverse issues have been worked out this should be relatively easy to tackle. (knowing the inverse relationship is really the only way to do this reliably)

    The basic approach will be that when appending a child to a collection of children, the child will be related to the parent (child.parent.should equal(parent)). Conversely, when assigning a parent to a child, we will do parent.children << child behind the scenes -- provided that the child does not already exist in the collection.

  • Dan Kubb (dkubb)

    Dan Kubb (dkubb) July 7th, 2009 @ 10:33 AM

    That's interesting. It might be a problem, but I'm not sure yet. I'll have to see if I can put together a few models with ambiguous relationships and see if I can reason out how to handle it.

  • Dan Kubb (dkubb)

    Dan Kubb (dkubb) October 4th, 2009 @ 09:33 PM

    • Milestone changed from 0.10.0 to 0.10.2

    [project:id#20609 not-tagged:"0.10.0" milestone:id#51895 bulk edit command]

  • Dan Kubb (dkubb)

    Dan Kubb (dkubb) February 1st, 2010 @ 04:33 PM

    • Milestone changed from 0.10.2 to 1.0.0
  • Dan Kubb (dkubb)
  • Dan Kubb (dkubb)
  • Dan Kubb (dkubb)

    Dan Kubb (dkubb) February 2nd, 2010 @ 02:47 AM

    • State changed from “accepted” to “confirmed”
  • Dan Kubb (dkubb)

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

    Ashely, I only really follow the tickets I or other people are actively working on. When I finish a ticket, and have available bandwidth I generally tackle the issues that affect my work, and if there aren't any of those, tickets that are most often reported.

    While this can bite people, I've only heard it reported a couple of times in several years .. if there were more +1's or mentions on the mailing list, IRC or elsewhere it would certainly get bumped up in priority.

    Also lately I've had to cut my time spent on DM from 60-80 hours a week down to 5-10, and am hoping that the community will step up and help fix simpler issues like this. It's not sustainable for (almost) every fix to be dependent on me, and by pulling back I am starting to see others in the community help with fixes and improvements like snusnu and solnic, among several others.

    I will add a comment in #1042

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 »

People watching this ticket

Pages