Model transactions

I have three tables: groups, memberships, and users. When a user creates
a
group I need to make sure that a membership in that group is also
created. I
have considered using transactions but I keep seeing that these are
being
deprecated. Is there some other practice that is simpler or preferred?

Groups:
name
description
owner_id (user_id)
[has_many :users, through :memberships]
[has_many :memberships]

Memberships:
groups_id
user_id
confirmed_at
[belongs_to :groups]
[belongs_to :users]

Users:
user_id
login:
[has_many :groups, through membership]
[has_many :memberships]


David Andrew T.
http://dathompson.blogspot.com

Since Memberships is essentially a join table with an extra attribute,
and I see that you already have your :through relationships defined,
simply doing

@group = Group.new( params[:group] )
@user.groups << @group

will create the membership record (assuming that @user is the User
object).

Rein

On Sep 1, 12:51 pm, “David Andrew T.”

I see on my second reading that you want to both set the owner_id of
the group and create a membership. This is a bit more complicated (but
not much!)

You will need to specify the association between Group and User:

class Group < AR::Base

belongs_to :owner, :class_name => “User”, :foreign_key => “owner_id”

end

class User < AR::Base

has_many :owned_groups, :class_name => “Group”, :foreign_key =>
“owner_id”

end

then your code to create a group belonging to this user could be:

@group = Group.new( params[:group] )
@group.owner = @user
@user.groups << @group

You could also use scoping:

@user.owned_groups.build( params[:group] )
@user.groups << @group

The key is that @user.owned_groups as set up here are the groups where
the group’s owner_id == @user.id and @user.groups are the groups that
the @user is a member of via the memberships join table.

As far as I can see, it is better to use has_and_belongs_to_many to
define the relationship of user and group.

On Sep 1, 1:51 pm, “David Andrew T.”

David,

There are two relationships between user and group:

One is a many to many relationship (the concept of membership), one is
a one to many (the concept of ownership).

The many to many is already expressed via a has_many :through with a
Membership join model. This is roughly equivalent to a habtm but also
allows attributes on the join (like the confirmed_at timestamp)
because the join table is represented as a first order class via the
Membership model. Habtm would not be sufficient for this relationship.

The other is the “ownership” of the group, as expressed with the
owner_id foreign key. This is a completely separate relationship and
needs to be specified as a has_many/belongs_to.

I hope this better explains the reasoning behind the relationships I
outlined above.

David Andrew T. wrote:

I have three tables: groups, memberships, and users. When a user
creates a group I need to make sure that a membership in that group is
also created. I have considered using transactions but I keep seeing
that these are being deprecated.

IIRC transactions aren’t deprecated only the form where the rollback not
only applies to the DB but to the ActiveRecord::Base objects too.

The day ActiveRecord::Base completely removes transaction support is the
day someone will fork it or provide the feature as a plugin :slight_smile:

Lionel

I would encapsulate all of this on the user model.

class Group < AR::Base
belongs_to :owner, :class_name => “User”, :foreign_key => “owner_id”
end

class User < ActiveRecord::Base
has_many :groups, :through => :memberships
has_many :memberships

def create_group(options)
g = Group.new(options.merge({ :owner => self }))
groups << g if g.save
g
end
end

Your controller code becomes the even simpler:

@group = @user.create_group params[:group]

Now you’ve got a method which explains what you really want to do,
and handles it without the application code worrying about the
details.

Pat

You are correct. My code above for creating and assigning a group can
be wrapped in a

transaction do

[code here]
end

block to wrap it in a transaction to provide rollback if any of the
operations fails.

It is the Model.transaction form that is deprecated. See

On Sep 2, 4:01 pm, Lionel B. [email protected]

Pat,

That’s an excellent intention-revealing abstraction and a great
example of the smart model, dumb controller design paradigm.

Rein

Fantastic thread! Thanks so much everyone. This was immensely helpful.
-Dave

David Andrew T.
http://dathompson.blogspot.com