This project is archived and is in readonly mode.

#4553 ✓stale
demersus

habtm association failure to save in join table with after_create callback.

Reported by demersus | May 7th, 2010 @ 09:40 PM

I am writing a rails app that has an attachment model that holds a reference to an uploaded file. Attachment can habtm tags. The problem I am experiencing is that Activerecord ignores the fact that I have associated tags with the attachment when I call self.save! From inside an after_create callback.

The after_create callback is necessary to save the file with the id included in the filename, and to update the file path on the record. Here is an example:

# after_create callback
def save_file
  self.file_path = "#{@upload_dir}/#{self.id}_#{self.filename}"
  FileUtils.copy self.file.local_path, self.file_path
  self.save!
end

The after_create fires and works like it should, however - rails forgot that I had assigned tag_ids to the self.tag_ids property. (It will not perform the insert on the join table after completing the after_create callback.)

Comments and changes to this ticket

  • Neeraj Singh

    Neeraj Singh May 7th, 2010 @ 10:06 PM

    Can you provide a sample example with complete information so that I could reproduce the problem on my side.

    For example look at the way the author of this ticket has given a very clear cut way of reproducing the problem. https://rails.lighthouseapp.com/projects/8994/tickets/4329

    Thanks

  • demersus

    demersus May 7th, 2010 @ 10:45 PM

    Sorry, I do not have a real test case for it yet. (I am new to rails and TDD in general.)

    You can recreate the scenario by creating these simplified files:

    attachment.rb

    (attacment model)

    class Attachment < ActiveRecord::Base
      attr_accessor :temp_file
      after_create :save_file
    
      has_and_belongs_to_many :tags, :uniq => true
    
      upload_dir
        "/some/path/to/upload/dir/"
      end
      
      # virtual file fields (loaded by controller.)
      # *not actually a column in table.
      def file
        self.temp_file
      end
      def file=(file_field)
        self.file_name = file_field.original_filename
        self.mime_type = file_field.content_type.chomp
        self.file_size = file_field.size.to_i
        # store the file field in temp_file for later processing.
        self.temp_file = file_field
      end
    
      protected
      # after_create callback.
      def save_file
        self.file_path = "#{upload_dir}/#{self.id}_#{self.file_name}"
        FileUtils.copy self.file.local_path, self.file_path
        self.save!
      end
    end
    

    tag.rb

    • (Tag model) *
      class Tag < ActiveRecord::Base
      has_and_belongs_to_many :attachments, :uniq => true
      end
      

    migration file

    class CreateTables < ActiveRecord::Migration
      def self.up
        create_table :attachments do |t|
          t.string :name
          t.string :mime_type
          t.string :file_path
          t.string :file_name
          t.integer :file_size
          t.timestamps
        end
        create_table :tags do |t|
          t.string :name
        end
        create_table :attachments_tags, :id => false do |t|
          t.references :attachment
          t.references :tag
        end
      end
      # add self down code here
    end
    

    Now, I am not going to post the view code, but here are the params that I am posting up:

    attachment[name] = testfile
    attachment[file] = <standard file from file field in view>
    attachment[tag_ids][] = 1
    attachment[tag_ids][] = 2
    

    Assuming my attachments controller has a create method like this:

    def create
      @attachment = Attachment.new params[:attachment]
      if @attachment.save

    flash[:notice] = 'Attachment saved.'
    redirect_to some_restful_path()
    
    
    
    
    else
    flash.now[:warning] = 'Attachment failed to save.'
    render :new
    
    
    
    
    end end

    You would expect to look in your database after performing this action and see this table:
    [attachments_tags]

    | attachment_id | tag_id |
    |             1 |      1 |
    |             1 |      2 |
    

    But you don't. Its empty.
    * the model saves the file, and updates the record in the {attachments} table correctly. But the tag_ids are completely ignored.

  • demersus

    demersus May 7th, 2010 @ 10:50 PM

    • Tag changed from activerecord callback habtm save to 2.3.x, activerecord, after_create, habtm, save
  • Santiago Pastorino

    Santiago Pastorino February 2nd, 2011 @ 05:02 PM

    • State changed from “new” to “open”
    • Tag changed from 2.3.x, activerecord, after_create, habtm, save to 23x, activerecord, after_create, habtm, save

    This issue has been automatically marked as stale because it has not been commented on for at least three months.

    The resources of the Rails core team are limited, and so we are asking for your help. If you can still reproduce this error on the 3-0-stable branch or on master, please reply with all of the information you have about it and add "[state:open]" to your comment. This will reopen the ticket for review. Likewise, if you feel that this is a very important feature for Rails to include, please reply with your explanation so we can consider it.

    Thank you for all your contributions, and we hope you will understand this step to focus our efforts where they are most helpful.

  • Santiago Pastorino

    Santiago Pastorino February 2nd, 2011 @ 05:02 PM

    • State changed from “open” to “stale”
  • bingbing

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

People watching this ticket

Pages