Adding objects to an association

7 views
Skip to first unread message

MarkMT

unread,
Nov 17, 2008, 9:27:14 AM11/17/08
to DataMapper
I'm having some difficulty with associations in datamapper - hopefully
someone can show me where my misunderstanding lies...

I have two models related by a many to many association via a join
model.

---
class Subscription

include DataMapper::Resource

property :id, Serial

has n, :profiles
has n, :users, :through => :profiles
---
class User

include DataMapper::Resource

property :id, Serial

has n, :profiles
has n, :subscriptions, :through => :profiles
---
class Profile

include DataMapper::Resource

property :subscription_id, Integer, :key => true
property :user_id, Integer, :key => true

belongs_to :subscription
belongs_to :user
---

Then in the controller I attempt to instantiate a subscription and
user and associate them...

---
def some_action
@subscription = Subscription.create
@user = User.create
@subscription.users << @user
end
---

When I point my browser at this action, I get the following -

Immutable Association Error 500
You can not modify this association

I've tried the same association operation with ActiveRecord in Rails
and it works fine there. Evidently there's something different about
datamapper that I don't understand.

Any suggestions would be much appreciated.

Mark.

kristian

unread,
Nov 18, 2008, 7:28:41 AM11/18/08
to DataMapper
a workaround for this bug is to add :mutable => true as option, i.e.

has n, :users, :through => :profiles, :mutable => true

see also my comment here
http://wm.lighthouseapp.com/projects/4819/tickets/485-has-n-through-does-not-allow-saving-of-model#ticket-485-5

with regards
Kristian

MarkMT

unread,
Nov 18, 2008, 8:15:01 AM11/18/08
to DataMapper
Thanks Kristian. I'm glad there's a workaround. Pretty significant
issue imo.

Mark.


On Nov 18, 6:28 am, kristian <meier.krist...@gmail.com> wrote:
> a workaround for this bug is to add :mutable => true as option, i.e.
>
> has n, :users, :through => :profiles,  :mutable => true
>
> see also my comment herehttp://wm.lighthouseapp.com/projects/4819/tickets/485-has-n-through-d...

MarkMT

unread,
Nov 19, 2008, 9:38:03 AM11/19/08
to DataMapper
Ok, I'm still having some problems with this. :mutable => true
certainly solves the original problem, but when @subscription.users
<< @user executes, I'm not seeing any evidence that any join model
object is being saved.

Has anyone else been successful in using has n, :though ?

When I look at the integration spec, association_through_spec.rb, I
see that the examples all involve _explicitly_ creating the join
model:

---
crappy = Tagging.new
post.taggings << crappy
post.save

crap = Tag.create(:title => "crap")
crap.taggings << crappy
crap.save
---

Is this what you have to do in datamapper?? This is definitely not
required in activerecord.

Mark.

Dirkjan Bussink

unread,
Nov 19, 2008, 10:41:09 AM11/19/08
to datam...@googlegroups.com
> crappy = Tagging.new
> post.taggings << crappy
> post.save

This saving will always be required, because adding a new object to an
association should never have the side effect of actually saving the
Tagging object here too. That would be really unexpected imho.

For us, ActiveRecord is not the default standard on how things behave.
We like to create something that fits the Ruby idiom of behaving like
a developer expects. Saving objects as a side-effect of just adding it
to an association does not really fit that principle of least
surprise.

These things notwithstanding, the has :through code is not up to par,
so there probably will be issues. We're not yet sure on how to solve
all of these problems, so that will take some discussion too.

--
Regards,

Dirkjan Bussink

MarkMT

unread,
Nov 19, 2008, 4:21:04 PM11/19/08
to DataMapper
I guess least surprise is in the eye of the beholder. My $0.02 is that
if I declare that model Post:

has n, :tags, :through :taggings

I have plainly stated an intention that the only way an object of
class Tag can be associated with one of class Post is by creating an
instance of a join model Tagging. So if I then say

post.tags << some_tag

(if in fact you are going to allow that) imo there is no way to
interpret this as meaning something other than an intention to create
a join model instance. Making me explicitly create the join instance
and associate it separately in each direction seems like unnecessary
repetition to me. Also, I assume (though haven't tested) that if I use
the form :through => Resource, the (in this case) anonymous join model
does actually get created and saved automatically when I do an
association across the :through relationship. Is this true?

Without providing this behavior, I don't really see why datamapper
would bother offering the :through option. Am I missing something?

Mark.

MarkMT

unread,
Nov 19, 2008, 5:32:01 PM11/19/08
to DataMapper
BTW, I meant to mention - although I realize there's no fundamental
reason to view ActiveRecord as the default standard, there has been
some indication in the publicity for Datamapper that it's drop-in
compatible. See slide 9 from Yehuda's talk at RailsConf:
http://schulty.com/articles/datamapper_presented_at_railsconf08.html.
This seems a little misleading.

Mark.

Dirkjan Bussink

unread,
Nov 19, 2008, 5:50:44 PM11/19/08
to datam...@googlegroups.com

On 19 Nov 2008, at 23:32, MarkMT wrote:

> BTW, I meant to mention - although I realize there's no fundamental
> reason to view ActiveRecord as the default standard, there has been
> some indication in the publicity for Datamapper that it's drop-in
> compatible. See slide 9 from Yehuda's talk at RailsConf:
> http://schulty.com/articles/datamapper_presented_at_railsconf08.html.
> This seems a little misleading.

Technically, that's not what it says. It says you can drop it into
Rails by 1.0 and use it there, not that it's an actual replacement for
ActiveRecord. Maybe the phrasing is open for questioning, but I'm
pretty sure this us is what Yehuda means to say.

On the many to many issue, there are quite a few parts in it's
behavior that haven't been thought through completely, so it's like
some behavior is going to change. The goal will always be to be clear
on what happens and be consistent with other behaviors in DM.

--
Regards,

Dirkjan Bussink

MarkMT

unread,
Nov 19, 2008, 6:11:42 PM11/19/08
to DataMapper


On Nov 19, 4:50 pm, Dirkjan Bussink <d.buss...@gmail.com> wrote:
> On 19 Nov 2008, at 23:32, MarkMT wrote:
>
> > BTW, I meant to mention - although I realize there's no fundamental
> > reason to view ActiveRecord as the default standard, there has been
> > some indication in the publicity for Datamapper that it's drop-in
> > compatible. See slide 9 from Yehuda's talk at RailsConf:
> >http://schulty.com/articles/datamapper_presented_at_railsconf08.html.
> > This seems a little misleading.
>
> Technically, that's not what it says. It says you can drop it into  
> Rails by 1.0 and use it there, not that it's an actual replacement for  
> ActiveRecord. Maybe the phrasing is open for questioning, but I'm  
> pretty sure this us is what Yehuda means to say.

Ok thanks. That seems a little subtle to me. I think what you're
saying is you'll be able to use it in Rails, but you may not be able
to use it exactly the same way you would ActiveRecord. Anyhow, my
point was just to highlight that there is some somewhat ambiguous
messaging floating around.

Dan Kubb (dkubb)

unread,
Nov 20, 2008, 2:07:50 AM11/20/08
to DataMapper
Hi Mark,

> I guess least surprise is in the eye of the beholder. My $0.02 is that
>
> ...
>
> post.tags << some_tag
>
> (if in fact you are going to allow that) imo there is no way to
> interpret this as meaning something other than an intention to create
> a join model instance.

The behavior that DM associations are going to have (and may not now)
is that association << resource will change the state of the objects
in-memory, creating join instances when possible (more on that
later). It won't necessarily persist those changes until you
explicitly call save. This is consistent with other parts of DM, such
as assigning a value to an attribute (resource.attribute = value) --
this won't cause a change in the data store until you save the
resource.

I think that's what Dirkjan was referring to when he mentioned POLS.

> Making me explicitly create the join instance
> and associate it separately in each direction seems like unnecessary
> repetition to me.

This is not going to be the case permanently. We are actively working
on resolving this. The only reason it probably wasn't added is
because it's a bit difficult to do properly in all cases, especially
when multiple :through associations are chained together.

There are even cases where it's impossible to automatically create a
valid join instance, like say you have a required property without a
default. In those cases you'd need to create the join instance
explicitly. (although there are work-arounds to this, IMHO they are
even less elegant)

> Without providing this behavior, I don't really see why datamapper
> would bother offering the :through option. Am I missing something?

The main reason for using :through right now is that reads seem to
work ok. It's writes that kind of suck, although in general I would
avoid :through associations unless you don't mind the pain. This will
not be the case in the near future though.

Dan
(dkubb)

MarkMT

unread,
Nov 20, 2008, 7:21:20 AM11/20/08
to DataMapper
Thanks Dan. That clarifies and all seems quite reasonable. I'm happy
with having to save the association if the join object is created
automatically. I'm assuming this would be done by calling save on the
receiving object, i.e.

post.tags << some_tag
post.save

I'll be eagerly looking forward to these changes emerging in due
course.

Mark.
Reply all
Reply to author
Forward
0 new messages