Hey Jonathan, You were absolutely right. What I didn't mention before (because I didn't think it was relevant) is that all the code I've been describing is in the context of RSpec tests. After your last message, I took a look at the fixture in my before method. It turns out that for the children I had provided my own id value before asking DataMapper to create the entries that I later attempt to update and verify during the course of my tests. I removed the ids, refactored my tests, and refactored my code to be tighter as you suggested, and now everything works just fine without a need to reset anything.
It may very well be that my code would've worked during the course of "real" operation because I wouldn't have done any hardcoded creation of entities, but testing things with RSpec and applying your guidance taught me a lot. Thanks. On Jan 15, 11:42 am, Jonathan Stott <[email protected]> wrote: > Hi Neil > > That all looks like it should work. How did this stuff get into the > DB in the first place, then? That now seems like the most likely > source of the error. > > Also, you can simplify 'create_child_from' somewhat by doing: > > def self.create_child_from(name) > Child.new(:name => name) > end > > You could even do this: > > parent.children.new(:name => name) > > to pre-assign the parent. > > Regards > Jonathan > > On 15 January 2012 15:42, Neil Chaudhuri <[email protected]> wrote: > > > > > > > > > Hey Jonathan, > > > I took a look at what I'm doing with ids, and I don't think I am > > messing with ids at all. In my update of the existing children, I > > don't update their ids. When I create the new child instance in > > memory, I don't give it an id at all because the id property is a > > Serial type in my DataMapper model. > > > I've talked enough, so here is some code: > > > def self.update_children(parent, children_hash) > > parent.children.each do |child| > > name_to_status_hash = children_hash[child.id.to_s] > > unless name_to_status_hash.nil? > > updated = child.update( > > { > > :status => name_to_status_hash[child.name] > > }) > > end > > end > > end > > > where children_hash looks like this: > > > children_hash = { > > "1"=>{"Bobby"=>"true"}, > > "2"=>{"Sue"=>"false"} > > } > > > As you can see, I am iterating through the child collection from the > > parent, and for each child's id I am grabbing the value from the > > children_hash provided by a controller and updating only the status. > > Not touching the id. > > > Now here is the code for creating a new child: > > > def self.create_child_from(name) > > child = Child.new > > child.attributes = { > > :name => name > > } > > child > > end > > > Only name is required, so I populate that. But you'll notice no id > > value. > > > So I am still fairly confused. At least the code works with the > > sequence reset! > > > Thanks. > > > On Jan 15, 5:35 am, Jonathan Stott <[email protected]> wrote: > >> Hi Neil > > >> From the sequence of events you've described below, it seems the > >> problem is that one of your updates of the existing children is > >> updating the record id too and putting that beyond the end of the > >> sequence. Or that one of your create_child_from(data) calls is > >> creating a record with the same. DataMapper doesn't stop you from > >> doing this, as it's valid in SQL, and the authors assume if you want > >> to do it, you have good reason. > > >> In terms of documentation being dated, I wouldn't worry too much. > >> DataMapper takes the stability of its API /very/ seriously. The > >> public behaviour of the save() and update() methods definitely hasn't > >> changed since 1.0.0 was released (in 2010) and was pretty stable for a > >> long time before that. > > >> As for a more 'elegant' way of doing this, I'm not sure that there is > >> one. Wrapping the whole thing in a transaction might improve your > >> storage efficiency a little bit. Also, looking more toward the > >> future, updating all the attributes via "resource.attributes = > >> new_values" and then triggering the update process with a save of the > >> parent right at the end might allow an adapter to generate a large sql > >> call instead of lots of little ones, but that's hypothetical. > > >> Regards > >> Jonathan > > >> On 15 January 2012 04:05, Neil Chaudhuri <[email protected]> wrote: > > >> > Hey Jonathan, > > >> > Assume I have a Parent instance fetched from the database (Postgres) > >> > and it has some existing Child instances (as the Parent and Child > >> > instances were all created at the same time). The first thing I did is > >> > call > > >> > existing_parent.update(new_parent_values) > > >> > ...which updates values in the Parent instance but nothing related to > >> > the children. > > >> > Then to update the children I did this > > >> > existing_parent.children.each do |child| > >> > updated = child.update( { #values }) > >> > end > > >> > ...which makes some changes to the existing children that are > >> > persisted just fine. > > >> > Then I want to add a NEW child to the children collection, which I did > >> > this way > > >> > existing_parent.children << create_child_from(data) > >> > #create_child_from(data) creates a new Child instance in memory and > >> > populates it with data > >> > existing_parent.save > > >> > It is on this third operation where I get the sequence error. > > >> > Executing the reset sequence statement beforehand does indeed help, so > >> > that solves the problem technically. > > >> > I would still love to know if there is a more efficient or elegant way > >> > to do the same sort of thing--in fact, for all the operations of > >> > updating the parent, then updating the existing children, then adding > >> > a brand new child. The documentation I've been reading seems a bit > >> > dated. > > >> > Thanks. > > >> > On Jan 14, 8:39 am, Jonathan Stott <[email protected]> wrote: > >> >> Hi Neil > > >> >> Errors like that tend to occur if you've imported a whole bunch of > >> >> pre-existing records into the database/table without updating the > >> >> sequence. Ideally you should never need to run it, but if you have > >> >> to, the time to do it is just after you've performed the DB import > >> >> task. If you haven't just done that, can you explain what you did > >> >> before the errors occured? > > >> >> I'm not sure DataMapper has any explicit support for this. > > >> >> Regards > >> >> Jonathan > > >> >> On 14 January 2012 00:54, Neil Chaudhuri <[email protected]> wrote: > > >> >> > Apparently, the reason why updates won't save is a sequence issue as > >> >> > found > >> >> > here:http://railspikes.com/2009/3/6/duplicate-key-violates-unique-constraint > > >> >> > So I suppose I need to do something like this: select > >> >> > setval('children_id_seq', (select max(id) + 1 from children)); > > >> >> > I have two questions as I am not a DBA: > > >> >> > 1) When exactly do I do this? Every time I want to insert a child > >> >> > record? > >> >> > 2) Why doesn't DataMapper handle this? Maybe I am spoiled by using > >> >> > Hibernate in the Java world, but they handle sequences fairly easily > >> >> > by using what they call dialects. I assumed the database-specific > >> >> > DataMapper gems would do something similar. Or maybe they do and I am > >> >> > just missing it? > > >> >> > Thanks. > > >> >> > On Jan 13, 1:14 pm, Neil Chaudhuri <[email protected]> wrote: > >> >> >> I have been able to create a 1:M relationship using DataMapper and > >> >> >> update the child items as well after creation. However, I have been > >> >> >> unable to add to that collection. > > >> >> >> Let's say I have > > >> >> >> class Parent > >> >> >> include DataMapper::Resource > > >> >> >> property :id, Serial > >> >> >> ... > > >> >> >> has n, :children > >> >> >> end > > >> >> >> class Child > >> >> >> include DataMapper::Resource > > >> >> >> property :id, Serial > >> >> >> ... > >> >> >> belongs_to :parent > >> >> >> end > > >> >> >> Whenever I try to add to the child collection belonging to a certain > >> >> >> parent instance, I get either DataMapper::UpdateConflictError or > >> >> >> "DataObjects::IntegrityError: ERROR: duplicate key value violates > >> >> >> unique constraint "children_pkey" DETAIL: Key (id)=(1) already > >> >> >> exists. > > >> >> >> I have tried a whole host of approaches. The most promising seemed > >> >> >> parent.children.create(attributes), but that led to the > >> >> >> IntegrityError. What is the preferred approach for adding a new item > >> >> >> to the collection? > > >> >> >> Thanks. > > >> >> > -- > >> >> > You received this message because you are subscribed to the Google > >> >> > Groups "DataMapper" group. > >> >> > To post to this group, send email to [email protected]. > >> >> > To unsubscribe from this group, send email to > >> >> > [email protected]. > >> >> > For more options, visit this group > >> >> > athttp://groups.google.com/group/datamapper?hl=en. > > >> > -- > >> > You received this message because you are subscribed to the Google > >> > Groups "DataMapper" group. > >> > To post to this group, send email to [email protected]. > >> > To unsubscribe from this group, send email to > >> > [email protected]. > >> > For more options, visit this group > >> > athttp://groups.google.com/group/datamapper?hl=en. > > > -- > > You received this message because you are subscribed to the Google Groups > > "DataMapper" group. > > To post to this group, send email to [email protected]. > > To unsubscribe from this group, send email to > > [email protected]. > > For more options, visit this group > > athttp://groups.google.com/group/datamapper?hl=en. -- You received this message because you are subscribed to the Google Groups "DataMapper" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/datamapper?hl=en.
