Carl Youngblood
2005-Oct-26 18:47 UTC
habtm with additional attributes that need to be changed after association is made
Hello, I''m writing an app with an habtm relationship like so:
class User < ActiveRecord::Base
has_and_belongs_to_many :resumes
end
class Resume < ActiveRecord::Base
has_and_belongs_to_many :users
def mark_as_viewed(user_id)
# what to put here?
end
end
The joining table has an extra attribute that holds a 1 or a 0
indicating whether or not this resume has been viewed already by this
user:
CREATE TABLE resumes_users
(
resume_id INT UNSIGNED NOT NULL DEFAULT 0,
user_id INT UNSIGNED NOT NULL DEFAULT 0,
viewed INT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (resume_id, user_id)
);
I want to be able to associate resumes with users beforehand and then
mark them as view at a later time. I saw the method
push_with_attributes for creating an association and adding
information at the same time. What is the best way to do this after
an association has already been created?
Thanks,
Carl
Jamie Macey
2005-Oct-26 21:08 UTC
Re: habtm with additional attributes that need to be changed after association is made
The consensus from when I had this question a few months ago (July/August) was to make a separate model for your habtm relation. In my case I had a Subscriber and a List, and wanted to track extra data about their relation, and the best way to do it was to create a Subscription model, which has_a :subscriber and has_a :list. Both Subscriber and List then has_many :subscriptions. For your data, I don''t know what I''d call it, but breaking the relation out into a separate class makes the metadata management much more palatable. - Jamie On Wed, 2005-26-10 at 11:47 -0700, Carl Youngblood wrote:> Hello, I''m writing an app with an habtm relationship like so: > > class User < ActiveRecord::Base > has_and_belongs_to_many :resumes > end > > class Resume < ActiveRecord::Base > has_and_belongs_to_many :users > > def mark_as_viewed(user_id) > # what to put here? > end > end > > The joining table has an extra attribute that holds a 1 or a 0 > indicating whether or not this resume has been viewed already by this > user: > > CREATE TABLE resumes_users > ( > resume_id INT UNSIGNED NOT NULL DEFAULT 0, > user_id INT UNSIGNED NOT NULL DEFAULT 0, > viewed INT UNSIGNED NOT NULL DEFAULT 0, > PRIMARY KEY (resume_id, user_id) > ); > > I want to be able to associate resumes with users beforehand and then > mark them as view at a later time. I saw the method > push_with_attributes for creating an association and adding > information at the same time. What is the best way to do this after > an association has already been created? > > Thanks, > > Carl > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails
Carl Youngblood
2005-Oct-26 21:43 UTC
Re: habtm with additional attributes that need to be changed after association is made
Thanks for the tip, but do think this is a good idea even if my model will only contain one single boolean variable? In that case, don''t you think it makes more sense to keep it in the association table? On 10/26/05, Jamie Macey <jamie-7g3wz9A/6AxWk0Htik3J/w@public.gmane.org> wrote:> The consensus from when I had this question a few months ago > (July/August) was to make a separate model for your habtm relation. > > In my case I had a Subscriber and a List, and wanted to track extra data > about their relation, and the best way to do it was to create a > Subscription model, which has_a :subscriber and has_a :list. Both > Subscriber and List then has_many :subscriptions. > > For your data, I don''t know what I''d call it, but breaking the relation > out into a separate class makes the metadata management much more > palatable.
Michael Smedberg
2005-Oct-26 22:22 UTC
Re: habtm with additional attributes that need to be changed after association is made
Here''s a copy of an email I sent earlier about this problem:
I had the same problem, and added a function to habtm to handle this. I
submitted it to the rails team as ticket 2462. The source code was:
module ActiveRecord
module Associations
class HasAndBelongsToManyAssociation
def update_attributes(record, join_attributes = {})
# Did they pass in an ID or an object?
if record.is_a? ActiveRecord::Base
# Check the type of the passed in record
raise_on_type_mismatch(record)
# Find the actual record in @target, if @target is loaded
if loaded?
record_in_arr = @target.find { | item | item == record }
raise ActiveRecord::RecordNotFound, "#{record.class}
#{record.id<http://record.id/>}
not found in collection" unless !record_in_arr.nil?
record = record_in_arr
record_id = record.id <http://record.id/>
else
record_id = record.id <http://record.id/>
record = nil
end
else
# The record isn''t an ActiveRecord, assume it''s an ID
record_id = record.to_i
# If the target is loaded, find the record in the target
if loaded?
# Find the actual record in @target
record_in_arr = target.find { | item | item.id <http://item.id/> == record
}
raise ActiveRecord::RecordNotFound, "Item with ID #{record} not found in
collection" unless !record_in_arr.nil?
record = record_in_arr
else
# Not loaded- for performance, don''t load it, just do an update based
on the
ID
record = nil
end
end
# Break the join_attributes into columns and values for those columns
cols_and_vals = join_attributes.to_a.transpose
# Join the columns together with '' = ?, '', so the result for
[a, b]
# would be ''a = ?, b'' NOTE: We will have to add a trailing
'' = ?''
# in the SQL
col_string = cols_and_vals[0].join('' = ?, '')
#NOTE: :before_update doesn''t do anything right now- this is
"future
proofing"
callback(:before_update, record) if !record.nil?
# Do the SQL, passing in the args
@owner.connection().update(sanitize_sql(["UPDATE #{@join_table} SET
#{col_string} = ? WHERE #{@association_class_primary _key_name} = ? AND
#{@association_foreign_key} = ?", cols_and_vals[1],
@owner.id<http://owner.id/>,
record_id].flatten), "Update Attributes")
# Fix up @target, the array of items IF it''s loaded
join_attributes.each { | att, att_val | record[att] = att_val } if
!record.nil?
#NOTE: :after_update doesn''t do anything right now- this is
"future
proofing"
callback(:after_update, record) if !record.nil?
end
end
end
end
which I added to application_helper.rb in my app.
Also see the thread with subject "*[Q] habtm with extra attributes*"
on this
forum, which discusses this a bit.
I''ve updated the ticket a couple times, adding functionality, etc.
On 10/26/05, Carl Youngblood
<carl-MJzSGySFh6ZUfOvSQQQpYw@public.gmane.org>
wrote:>
> Thanks for the tip, but do think this is a good idea even if my model
> will only contain one single boolean variable? In that case, don''t
> you think it makes more sense to keep it in the association table?
>
> On 10/26/05, Jamie Macey
<jamie-7g3wz9A/6AxWk0Htik3J/w@public.gmane.org> wrote:
> > The consensus from when I had this question a few months ago
> > (July/August) was to make a separate model for your habtm relation.
> >
> > In my case I had a Subscriber and a List, and wanted to track extra
data
> > about their relation, and the best way to do it was to create a
> > Subscription model, which has_a :subscriber and has_a :list. Both
> > Subscriber and List then has_many :subscriptions.
> >
> > For your data, I don''t know what I''d call it, but
breaking the relation
> > out into a separate class makes the metadata management much more
> > palatable.
> _______________________________________________
> Rails mailing list
> Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org
> http://lists.rubyonrails.org/mailman/listinfo/rails
>
_______________________________________________
Rails mailing list
Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org
http://lists.rubyonrails.org/mailman/listinfo/rails