Priyank
Priyank

Reputation: 69

Issue occurs due to "accepts_nested_attributes_for" and "has_many through" relationship which gives not id found error with factory_girl and rspec

Following are models:

class User < ActiveRecord::Base
   has_many :companies_users
   has_many :companies, :through => :companies_users
end

class Company < ActiveRecord::Base
   has_many :companies_users
   has_many :users, :through => :companies_users

   accepts_nested_attributes_for :users
   attr_accessible :name, :address_1, :address_2, :area, :city, :state, :zipcode, :country, :users_attributes

   after_create :create_subscriptions

   def create_subscriptions
     subscription=Subscription.create(:company_id => self.id, :subscription_dt => Date.today, :is_active => 'Y', :user_id => self.users.first.id)
     subscription.save
   end      

end

class CompaniesUser < ActiveRecord::Base
    belongs_to :user
    belongs_to :company 
end

Following are spec/factories/factory.rb

FactoryGirl.define do

  factory :company do |f|
    f.name "TestCompany"
    f.domain_url "test_url"
    users {|t| [t.association(:user)] } 
  end

  factory :user do |f| 
    f.first_name "John"
    f.last_name "Doe"
    f.password  "password"
    f.email "[email protected]"
    f.mobile_no "25589875"
    f.fax_no  "25548789"
    f.office_no  "25578455"     
  end

  factory :companiesuser do |f|
    association :user
    association :company
  end

end

Following is my spec/model/company_spec.rb

 context "Check methods" do

    it "check after create methods" do     
      company = FactoryGirl.create(:company)
    end

  end

While executing above company_spec it creates an issue due to method subscription which exist in company model and call after create callback create_subscriptions.which require self.users.first.id which it did not get and provide me following error.

$ rspec spec/models/company_spec.rb 
F

Failures:

  1) Company Model: Check methods check after create methods
     Failure/Error: company = FactoryGirl.create(:company)
     RuntimeError:
       Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
     # ./app/models/company.rb:47:in `create_subscriptions'
     # ./spec/models/company_spec.rb:45:in `block (3 levels) in <top (required)>'

Can anyone let me know what i need to do or its any association related issue? it create problem because first it enter values in company but not able to enter value in users table so not get user id which required in subscription method.

Upvotes: 2

Views: 497

Answers (2)

Priyank
Priyank

Reputation: 69

I resolved above issue by change code in "spec/model/company_spec.rb" as follow:

context "Check methods" do    
    it "check after create methods" do
      company = create(:company,"name"=>"mycom","domain_url"=>"test","users_attributes"=>{"0"=>{"email"=>"[email protected]","password"=>"password","password_confirmation"=>"password"}})
    end
  end

It created data in my join table also and worked successfully. Obviously its not done by factory join. I directly pass the user attributes here. So removed line

users {|t| [t.association(:user)] }

from company's factory.

Upvotes: 1

Richard Peck
Richard Peck

Reputation: 76774

I've done a nested has_many :through and you have to basically pass the attributes to the :companies_users model and then to the :users model


My Code (Rails 4.0)

Form

#views/admin/posts/new
<%= form_for [:admin, resource], :html => { :multipart => true }  do |f| %>
    <table class="resource_table">
        <thead>
            <th colspan="2"><%= params[:action].capitalize %> <%= resource_class %></th>
        </thead>
        <tbody class="form">
            <% attributes.each do |attr| %>
                <tr class="<%= cycle('odd', '')%>">
                    <td><%= resource_class.human_attribute_name(attr) %></td>
                    <td>
                        <% if attr == "body" %>
                            <%= f.text_area attr, :rows => 60, :cols => 80, :class => "redactor" %>
                        <% else %>
                            <%= f.text_field attr, :value => resource.public_send(attr).to_s %>
                        <% end %>
                    </td>
                </tr>
            <% end %>
            <%= f.fields_for :images_posts do |images_posts| %>
                <%= images_posts.fields_for :image do |images| %>
                    <tr>
                        <td>Image</td>
                        <td><%= images.file_field :image %></td>
                    </tr>
                <% end %>
                <tr>
                    <td>Caption</td>
                    <td><%= images_posts.text_field :caption %></td>
                </tr>
            <% end %>
            <tr class="dull">
                <td colspan="2"><%= f.submit "Go" %></td>
            </tr>
        </tbody>
    </table>
<% end %>

Models

#models/image_post.rb (the join model)   
class ImagePost < ActiveRecord::Base
        
        #Associations
        belongs_to :post, :class_name => 'Post'
        belongs_to :image, :class_name => 'Image'
        
        #Validations
        validates_uniqueness_of  :post_id, :scope => :image_id
        
        #Nested Association (Can upload & add images from form)
        accepts_nested_attributes_for :image, :allow_destroy => true
        
end


#models/image.rb
class Image < ActiveRecord::Base

    #Associations
    has_many :products, :class_name => 'Product', :through => :images_products, dependent: :destroy
    has_many :images_products, :class_name => 'ImageProduct'

    has_many :posts, :class_name => 'Post', :through => :images_posts, dependent: :destroy
    has_many :images_posts, :class_name => 'ImagePost'

    has_many :brands, :class_name => 'Brand', :through => :brands_images, dependent: :destroy
    has_many :brands_images, :class_name => 'BrandImages'

    #Image Upload 
    Paperclip.options[:command_path] = 'C:\RailsInstaller\ImageMagick'
    has_attached_file :image,
        :styles => { :medium => "x300", :thumb => "x100" },
        :default_url => "",
        :storage => :s3,
        :bucket => ''
        :s3_credentials => S3_CREDENTIALS

    #Validations
    validates_presence_of :image, :message => "No Image Present!"

end

#models/post.rb
class Post < ActiveRecord::Base

    #Images
    has_many :images, -> { uniq }, :class_name => 'Image', :through => :images_posts, dependent: :destroy
    has_many :images_posts, :class_name => 'ImagePost'

    #Nested Association (Can upload & add images from form)
    accepts_nested_attributes_for :images_posts, :allow_destroy => true

end

This was a BIG help for me: Rails nested form with has_many :through, how to edit attributes of join model?


Diagnosing Your Issue

I posted my code to give you a working example of what you can do (mine still needs refinement, but it works)

Looking at your code, you should add "accepts_nested_attributes_for" into your companies_users.rb model like this:

accepts_nested_attributes_for :user

In your companies model, change your accepts_nested_attributes_for to:

accepts_nested_attributes_for :companies_users

Upvotes: 0

Related Questions