Nick Tomlin
Nick Tomlin

Reputation: 29251

update column on join table

I have two sequel models user and event, which is currently handled by a single join table. Users can be invited to events, and these invitations have a status attached to them; this is handled by another join table.

class Event < Sequel::Model(:events)
  many_to_many :users
  many_to_many :invitations, :join_table => :events_invitations, :right_key => :user_id, :class => :User, :select=>[Sequel.expr(:users).*, :events_invitations__status]
end

class User < Sequel::Model(:users)
  many_to_many :events
  many_to_many :invitations, :join_table => :events_invitations, :right_key => :event_id, :class => :Event, :select=>[Sequel.expr(:events).*, :events_invitations__status]
end

I am able to create and access the associated invitations, but the changes are never saved (i'm assuming due to the way i've set up the many_to_many)

user = User.create(name: "bob")
event = Event.create(title: "super fete")
user.add_event(e)
event.add_invitation(e)

invite = user.invitations.first
# this returns a reference to the event, with the extra status column from the join table
# <Event @values={:id=>1, :title=>"fete", :owner_id=>nil, :user_id=>nil, :status=>"pending"}>
# does not actually save back to the table if I modify it...
invite[:status] = "accepted"

How should I model this so as to be able to see and set the status of invitations for users and events?

Upvotes: 1

Views: 84

Answers (1)

Phrogz
Phrogz

Reputation: 303321

First, some (ultimately unhelpful) basics.

Sequel is letting you treat the model like a hash, insofar as you can store extra read-only values on it. However, as with Sequel's hashes returned from a dataset, just setting the key does not update it. For example:

bob = DB[:users][name:'Bob']  #=> {:id=>1, :name=>"Bob"}
bob[:name] = 'Mary'
p bob                         #=> {:id=>1, :name=>"Mary"}
p DB[:users][1]               #=> {:id=>1, :name=>"Bob"}

This is true for Model instances as well:

bob = User[name:'Bob']  #=> #<User @values={:id=>1, :name=>"Bob"}>
bob[:name] = 'Mary'
p bob                   #=> #<User @values={:id=>1, :name=>"Mary"}>
p User[1]               #=> #<User @values={:id=>1, :name=>"Bob"}>
bob.name = 'Jerome'
p bob                   #=> #<User @values={:id=>1, :name=>"Jerome"}>
p User[1]               #=> #<User @values={:id=>1, :name=>"Bob"}>

For the Dataset, you need to update your dataset to change values in the database.

For the Model, you need to either update the values as above and then save your changes to that model instance (e.g. bob.save) or you need to use your instances to update it with new values (e.g. bob.update name:'Mary').


But really, you are abusing the many_to_many class and select values to make a thing that looks like an Event, but which is not. If 20 people are invited to the fête, it's incorrect to have 20 different "events" all named "super fete" but with differing status. Those aren't events, those are invitations.

You want an Invitation model to represent your join, associated with the join table. Then you can get an instance that has a status, and you can update it as you like. You can't update from the Event instance because it doesn't know anything about the events_invitations table or the status column, other than to fetch them when asked.

Upvotes: 1

Related Questions