accepts_nested_attributes_for called twice in model - ruby-on-rails

class StepQuiz < ActiveRecord::Base
belongs_to :step
has_many :step_quiz_questions, :dependent => :destroy
accepts_nested_attributes_for :step
accepts_nested_attributes_for :step_quiz_questions, :allow_destroy => true
attr_accessible :step_id, :instructions, :correct_to_pass, :retakes_allowed, :time_limit, :step_attributes, :step_quiz_questions_attributes
end
Am I allowed to have accepts_nested_attributes_for called twice for a given model. It appears to work with no errors.

You are using a class method defined here: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
The answer is yes, so that you can tune every single case.

Related

after_destroy not called for linked table

I have these models
class User < ActiveRecord::Base
has_many :user_functions, :dependent => :destroy
has_many :functions, :through => :user_functions
accepts_nested_attributes_for :functions, allow_destroy: true
Model of the linked table:
class UserFunction < ActiveRecord::Base
belongs_to :user, inverse_of: :user_functions
belongs_to :function, inverse_of: :user_functions
after_destroy :unplan_items
after_create :plan_items
and of course the model of function but this is like user...
Now when I do the following in my tests:
#user.functions = [#functions]
#user.save
expect(#user.planned_items.count).to eq(1)
#user.functions = []
#user.save
I notice the callback after_destroy isn't called. Why is this and how can I avoid this. There are certain steps that need to be done every time a UserFunction is destroyed...
I believe this has to do with: https://github.com/rails/rails/issues/7618 (I'm using rails 4.2.5 though). The after_create is working perfect though...
Currently rails uses :delete_all as default strategy of has_many_through. It only calls :destroy_all when we explicitly specify dependent: :destroy on the association.
The docs mention advice to use has_many :through if you need callbacks:
See the suggestion here: http://guides.rubyonrails.org/association_basics.html
You should use has_many :through if you need validations, callbacks,
or extra attributes on the join model.
So there currently is an inconsistency between after_create which does do the callback and after_destroy.
This is mentioned in these two issues posted on GitHub:
https://github.com/rails/rails/issues/7618
https://github.com/rails/rails/issues/27099
The fix for now is to explicitly put :dependent => :destroy on the :through part. This will make sure the callback are used.
class User < ActiveRecord::Base
has_many :user_functions
has_many :functions, :through => :user_functions, :dependent => :destroy
accepts_nested_attributes_for :functions, allow_destroy: true
For anyone reading this 2021+
Change This
has_many :object_tags, :as => :taggable, :dependent => :destroy
has_many :tags, :through => :object_tags
To This
has_many :object_tags
has_many :tags, :through => :object_tags, :dependent => :destroy

Reject nested assosciation creation rails

I have 2 models as below,
Updated based on suggestions
class User < ActiveRecord::Base
has_many :company_users, dependent: :destroy, inverse_of: :user
accepts_nested_attributes_for :company_users, allow_destroy: true
has_many :companies, through: :company_users
has_many :roles, through: :company_users
end
and
class CompanyUser < ActiveRecord::Base
belongs_to :company
belongs_to :role
belongs_to :user, inverse_of: :company_users
validates :user, uniqueness: {scope: [:company, :role]}
end
I find the uniqueness validation is working only on the update request. On create request validation is not functioning and it simply bypasses it.
I want to enable the same validation to reject if a user has same company & role assigned more than once.
If you want a ensure the uniqueness of user on unique pair of :company and :role, then you can try following. By default, the validations run for both create and update. You don't need :on => [ :create, :update ]. So it should be just:
validates :user, uniqueness: {scope: [:company, :role]}
Solved this issue with the below validation,
class User < ActiveRecord::Base
has_many :company_users, dependent: :destroy, inverse_of: :user
accepts_nested_attributes_for :company_users, allow_destroy: true
has_many :companies, through: :company_users
has_many :roles, through: :company_users
validate :company_users, :uniqueness_of_company_users
end
private
def uniqueness_of_company_users
errors.add(:company_users, 'error in role creation') if company_users.map{|x| "#{x.company_id} #{x.role_id}"}.uniq.size != company_users.size
end
This is the additional validation required to solve the issue
Thanks Rich Peck for this https://railscoding.wordpress.com/2015/04/27/uniqueness-gotcha/
From the docs
The :on option takes one of the values :create or :update
--
A validation is only run on create or update anyway, right?
find doesn't manipulate the db, destroy gets rid of the record & new just invokes a new instance of the object. You have literally zero other reasons to validate.
So, really, you should have:
validates :user, uniqueness: {scope: [:company_id, :role_id]}
This will look up against the values in company_id and role_id, which is probably going to be more efficient than calling the company and role objects themselves.
I could be wrong, but I really think if you used the above, it should work.
--
You may also wish to clean up your models:
class User < ActiveRecord::Base
has_many :company_users, dependent: :destroy, inverse_of: :user
accepts_nested_attributes_for :company_users, allow_destroy: true
has_many :companies, through: :company_users
has_many :roles, through: :company_users
end
class CompanyUser < ActiveRecord::Base
belongs_to :company
belongs_to :role
belongs_to :user, inverse_of: :company_users
validates :user, uniqueness: {scope: [:company_id, :role_id]}
end

Rails Accessing Polymorphic Data from a Relation

I have following models in a Rails application.
class Address < ActiveRecord::Base
belongs_to :addressable, :polymorphic => true
end
class Client < ActiveRecord::Base
has_one :address, :as => :addressable, dependent: :destroy
accepts_nested_attributes_for :address, :allow_destroy => true
has_many :invoices
end
class Invoice < ActiveRecord::Base
belongs_to :client
end
While I am able to retrieve the client name using
#invoice.client.name
I am not able to retrieve the address information in the similar manner.
How do I retrieve the address attributes in the view for invoice?
#invoice.client.address that is the aswer. But i recommend you follow the Law of Demeter using the method delegate
http://en.wikipedia.org/wiki/Law_of_Demeter
http://api.rubyonrails.org/classes/Module.html#method-i-delegate
Basically the idea is that you can do this: #invoice.client.address_street o better #invoice.client_address_street

Rails3 has_many through working on local but fail on heroku

i'm using sqlit3 for local and Postgre for heroku.
Everything works fine until i upload my files to heroku. Here is my model.
class User < ActiveRecord::Base
belongs_to :unit
has_friends
end
class Unit < ActiveRecord::Base
attr_accessible :unit, :floor
has_many :users
belongs_to :block
end
class Block < ActiveRecord::Base
attr_accessible :block, :units_attributes
has_many :units, :dependent => :destroy
accepts_nested_attributes_for :units, allow_destroy: true
belongs_to :postalcode
end
class Postalcode < ActiveRecord::Base
attr_accessible :postalcode, :blocks_attributes
has_many :blocks, :dependent => :destroy
accepts_nested_attributes_for :blocks, allow_destroy: true
belongs_to :neighbourhood
end
class Neighbourhood < ActiveRecord::Base
attr_accessible :name, :streetname, :postalcodes_attributes
has_many :postalcodes, :dependent => :destroy
has_many :blocks, :through => :postalcodes
has_many :units, :through => :blocks
has_many :users, :through => :units
accepts_nested_attributes_for :postalcodes, allow_destroy: true
validates :name, :presence => true
validates :streetname, :presence => true
end
i troubleshooted and found that the problem is with this method in the controller.
#neighbours = current_user.unit.block.postalcode.neighbourhood.users
Although #neighbours = current_user.unit.block.postalcode.neighbourhood works perfectly fine.
Please help, i'm desperate, i have tried googling for it one whole day.
Check out this answer to a similar issue
It is quite likely the error is coming up from WHERE "postalcodes"."neighbourhood_id" = 1 which indicates that neighbourhood_id in postalcodes table is created as a String, instead of an integer.
Follow the steps mentioned in the answer accordingly, and change it to an Integer.

Undefined method when accessing through association and uninitialized constant when trying to destroy with :dependent => :destroy

I've tried persistently googling this error, but to no avail. I currently have these models
app/models/survey.rb
class Survey < ActiveRecord::Base
belongs_to :user
has_attached_file :original, :default_url => "/public/:class/:attachment/:basename.:extension"
has_many :sub_surveys, :dependent => :destroy
end
app/models/sub_survey.rb
class SubSurvey < ActiveRecord::Base
belongs_to :survey
has_many :questions, :dependent => :destroy
end
app/models/question.rb
class Question < ActiveRecord::Base
belongs_to :sub_survey
validates_presence_of :sub_survey
acts_as_list :scope => :sub_survey
#after_destroy :destroy_orphaned_choices
has_many :answers, :dependent => :destroy
has_many :choices, :dependent => :destroy
end
app/models/choice.rb
class Choices < ActiveRecord::Base
belongs_to :question
validates_presence_of :question
end
app/models/answer.rb
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :user
belongs_to :game
validates_uniqueness_of :question_id, :scope => [:user_id, :game_id]
end
Now when I try to destroy a survey, I get an error
uninitialized constant Question::Choice
That traces through /vendor/rails/active* stuff after the survey.destroy
Then when I try to access choices from question.Choices, I get an error
undefined method `Choices' for #<Question:0xb7224f2c>
which for some reason has this on top of the trace-stack
vendor/rails/activerecord/lib/active_record/attribute_methods.rb:256:in `method_missing'
vendor/plugins/attribute_fu/lib/attribute_fu/associations.rb:28:in `method_missing'
app/views/answers/_answer.html.erb:7:in `_run_erb_47app47views47answers47_answer46html46erb'
I do use attribute_fu when importing surveys in xml-format, but I have no idea why the trace of question.Choices has it.
I also tried renaming choices to choicealternatives, but that didn't have an effect.
Any ideas?
Your Choices table has already got a pluralised name which may be causing problems. Ideally that table should be called Choice otherwise your has_many :choices should specify the class_name option too. E.g.
has_many :choices, :class_name => 'Choices'
Though I'd opt for renaming the class and table Choice if you can.
Attachment_fu is probably appearing in the stack trace because they have overridden or aliased the method_missing method to add their own behaviour. It's not necessarily anything to be concerned about.
I'm not sure why you get the error when destroying a Survey, but you're getting this
undefined method `Choices' for #<Question:0xb7224f2c>
because you should be accessing it like this:
question.choices # No capitalization
I think that should solve one of the problems.

Resources