Forum: Ruby on Rails habtm update attribute question

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
A2c85dc5ee81b12e3cc0a6522e8d079d?d=identicon&s=25 christopher.k.hall (Guest)
on 2005-11-14 20:30
(Received via mailing list)
given the following habtm relationship

users
----------
id
name

articles
----------
id
title

articles_users
----------
article_id
user_id
last_read
primary key (article_id, user_id)

class User < ActiveRecord::Base
has_and_belongs_to_many :articles

def read_article(article)
articles.push_with_attributes(article, :last_read => Time.now)
end
end

class Article < ActiveRecord::Base
has_and_belongs_to_many :users
end

i understand that push_with_attributes will create the entry in the join
table, and will also put the current time in the join table.

but what if i need to update the time? ie, i want to track not only that
the
article was read, but WHEN it was last read? would i still use
push_with_attributes or is there another mechanism i am not aware of
that I
can use to update the last_read column in the join table?

Chris
67b6389be42524fbd776e44fd35c3d7e?d=identicon&s=25 peter.j.donald (Guest)
on 2005-11-14 21:59
(Received via mailing list)
On 11/15/05, Chris Hall <christopher.k.hall@gmail.com> wrote:
>  but what if i need to update the time?  ie, i want to track not only that
> the article was read, but WHEN it was last read?  would i still use
> push_with_attributes or is there another mechanism i am not aware of that I
> can use to update the last_read column in the join table?

The path of least resistence is to change the relationship into a
model object. You would replace the HABTM with a has_many and create a
new "Reading" model object.

Hope that helps,

--
Cheers,

Peter Donald

RealityForge.org: http://www.realityforge.org/
3ec705c5dd3480c6268b72c5617e8dae?d=identicon&s=25 smedberg (Guest)
on 2005-11-15 00:08
(Received via mailing list)
I had the same problem. I solved it by adding an "update_attributes"
method
to HABTM. I added this code to application_helper.rb:



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_id }
raise ActiveRecord::RecordNotFound, "Item with ID #{record_id} not found
in
collection" if 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)

# Do the SQL, passing in the args
ret = @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)

return ret
end
end
end


class ActiveRecord::Base
#MES- CUSTOMIZATION: Making it so that HABTM can support before_update
and
after_update callbacks
# FROM active_record\associations.rb
def self.has_and_belongs_to_many(association_id, options = {},
&extension)
#MES- Note that the ONLY difference between this function and
# ActiveRecord::Associations::ClassMethods is that the validation
supports
# the :before_update, and :after_update option
options.assert_valid_keys(
:class_name, :table_name, :foreign_key, :association_foreign_key,
:conditions, :include,
:join_table, :finder_sql, :delete_sql, :insert_sql, :order, :uniq,
:before_add, :after_add,
:before_remove, :after_remove, :extend, :before_update, :after_update
)

options[:extend] = create_extension_module(association_id, extension) if
block_given?

association_name, association_class_name,
association_class_primary_key_name
=
associate_identification(association_id, options[:class_name],
options[:foreign_key])

require_association_class(association_class_name)

options[:join_table] ||=
join_table_name(undecorated_table_name(self.to_s),
undecorated_table_name(association_class_name))

add_multiple_associated_save_callbacks(association_name)

collection_accessor_methods(association_name, association_class_name,
association_class_primary_key_name, options,
HasAndBelongsToManyAssociation)

# Don't use a before_destroy callback since users' before_destroy
# callbacks will be executed after the association is wiped out.
old_method = "destroy_without_habtm_shim_for_#{association_name}"
class_eval <<-end_eval
alias_method :#{old_method}, :destroy_without_callbacks
def destroy_without_callbacks
#{association_name}.clear
#{old_method}
end
end_eval

add_association_callbacks(association_name, options)

# deprecated api
deprecated_collection_count_method(association_name)
deprecated_add_association_relation(association_name)
deprecated_remove_association_relation(association_name)
deprecated_has_collection_method(association_name)
end

def self.add_association_callbacks(association_name, options)
#MES- Note that the ONLY difference between this function and the
original
is the
# addition of the before_update and after_update callbacks
#callbacks = %w(before_add after_add before_remove after_remove)
callbacks = %w(before_add after_add before_remove after_remove
before_update
after_update)
callbacks.each do |callback_name|
full_callback_name =
"#{callback_name.to_s}_for_#{association_name.to_s}"
defined_callbacks = options[callback_name.to_sym]
if options.has_key?(callback_name.to_sym)
class_inheritable_reader full_callback_name.to_sym
write_inheritable_array(full_callback_name.to_sym,
[defined_callbacks].flatten)
end
end
end
end
1b6ed15a6126e1d5ef4d7d15550f30bb?d=identicon&s=25 François Montel (zerohalo)
on 2005-12-13 23:47
Would it be possible to give an example of how to call this method,
which parameters should be passed to it and using which syntax? Thanks!

smedberg wrote:
> I had the same problem. I solved it by adding an "update_attributes"
> method
> to HABTM. I added this code to application_helper.rb:
>
>
>
> module ActiveRecord
> module Associations
> class HasAndBelongsToManyAssociation
>
> def update_attributes(record, join_attributes = {})
This topic is locked and can not be replied to.