rails prevent deletion of child unless parent is being deleted also - ruby-on-rails-4

in Ruby on Rails 4, let's say a parent has many children. When the parent is deleted, the children must also be deleted. Other than that, the child shall not be deleted unless it is an orphan. How to do that?
I tried with the following
class Parent < ActiveRecord::Base
has_many :children, inverse_of: :parent, dependent: :destroy
end
class Child < ActiveRecord::Base
belongs_to :parent, inverse_of: :children
before_destroy :checks
private
def checks
not parent # true if orphan
end
end
With the before_destroy check, however, nothing gets deleted. Is there any way of telling this method if the reason of being called is because parent deletion?
Is this an odd thing to ask for? I mean, preventing deletion of childs.

Working from carp's answer from Rails: how to disable before_destroy callback when it's being destroyed because of the parent is being destroyed (:dependent => :destroy), try this:
Child:
belongs_to :parent
before_destroy :prevent_destroy
attr_accessor :destroyed_by_parent
...
private
def prevent_destroy
if !destroyed_by_parent
self.errors[:base] << "You may not delete this child."
return false
end
end
Parent:
has_many :children, :dependent => :destroy
before_destroy :set_destroyed_by_parent, prepend: true
...
private
def set_destroyed_by_parent
children.each{ |child| child.destroyed_by_parent = true }
end
We had to do this because we're using Paranoia, and dependent: delete_all would hard-delete rather than soft-delete them. My gut tells me there's a better way to do this, but it's not obvious, and this gets the job done.

Related

passing parameter in has_many through association

I have a class Test, Question, Testquestion associated by has_many through
class Test
has_many :testquestions
has_many :questions, through: :testquestions
end
class Question
has_many :testquestions
has_many :tests, through: :testquestions
end
class Testquestion
belongs_to :test
belongs_to :questions
end
While creating a Test i want to pass a value of column order of Testquestion.
def create
Test.create(test_params)
end
def test_params
params.require(:test).permit(:testname,question_attributes:[:questionname])
end
How should i pass value of order column so that associated model(Testquestion) get updated.
There's no way to do it that way, you will need to take a longer path.
Don't forget to add accepts_nested_attributes_for to your assossiations, so that nested operations would actualy work.
Check that you have added accepts_nested_attributes_for :question to Testquestion.
Get your params structured right.
Something like this:
{
test: {
testname: 'someName',
testquestion_attributes: {
order: someOrder,
question_attributes: {
questionname: 'someName'
}
}
}
}
Require your params.
params.require(:test).permit(:testname, testquestion_params: [:id, :order, :_destroy, question_params: [:id, :questionname])
p.s. sidenote: you should really aquire a habbit of naming your fields and variables in snake_case and your classes in CamelCase.

Retrieve method for a polymorphic, has_one and scoped association

Hi this is somewhat it trivial but I can't for the life of me figure out where to make the adjustments.
I have a LoanApplication model and a transfer model like this:
class LoanApplication < ActiveRecord::Base
before_save :populate_guid
belongs_to :user
has_one :loan, -> { where loan: true }, as: :transferable
has_one :repayment, -> { where repayment: true }, as: :transferable
validates_uniqueness_of :guid
private
def populate_guid
if new_record?
while !valid? || self.guid.nil?
self.guid = SecureRandom.random_number(1_000_000_000).to_s(36)
end
end
end
end
and
class Transfer < ActiveRecord::Base
belongs_to :transferable, polymorphic: true
belongs_to :user
validates_presence_of :transferable_id,
:transferable_type,
:user_id,
:amount,
:password
end
How come LoanApplication.first.loan gives me the following error message
LoanApplication Load (1.1ms) SELECT "loan_applications".* FROM "loan_applications" ORDER BY "loan_applications"."id" ASC LIMIT 1
NameError: uninitialized constant LoanApplication::Loan
All insights appreciated.
Thanks
I think Application is a reserved word. Try to rename LoanApplication?
It was trivial, I just needed to add class_name: "Transfer" for everything to work. -__-'

rails 4 - associations - is it possible to have a polymorphic association on one side of a many-through?

I have a Has-Many:Through association , in which one side (AccessLevel ) is a defined model 'AccessLevel' , but the other side can be set for multiple resources
I defined a specific concern to be included in these models, and tried to use a polymorphic association
class AccessLevel < ActiveRecord::Base
has_many :allowed_activities
has_many :actionable, through: :allowed_activities
end
class AllowedActivity < ActiveRecord::Base
belongs_to :access_level
belongs_to :actionable, polymorphic: true
end
class <Model> < ActiveRecord::Base
include Authorizable
end
module Authorizable
extend ActiveSupport::Concern
included do
has_many :allowed_activities, as: :actionable
has_many :access_levels, through: :allowed_activities
end
end
is that a correct implementation or am I going to raise some unexpected collateral issues ?
thanks for feedback
It's not possible to use has_many: :through on polymorphic object
I need to specify the source_type of each object ( Sheet, ...
has_many :actionable_sheets, :through => :allowed_activities, :source => :actionable, :source_type => 'Sheet'

SyntaxError on has_many association with block when trying to order

in Ruby on Rails 4, let's say a parent has many children. Then I wanted to reference only the persisted records in an active record association, and followed the link's accepted answer. This works good:
class Parent < ActiveRecord::Base
has_many :children, dependent: :destroy do
def persisted
collect { |a| a if a.persisted? }
end
end
Now, I want to order the associated records:
has_many :children, dependent: :destroy, -> { order 'id asc' } do
but this raises an error:
SyntaxError in ParentsController#index
...trunk/app/models/parent.rb:3: syntax error, unexpected keyword_do_block, expecting => ...trunk/app/models/parent.rb:49: syntax error, unexpected keyword_end, expecting end-of-input
However, this does work:
has_many :children, -> { order 'id asc' } do
I can't even find documentation on how to use the do_block on an association. Any help appreciated.
There was 2 problems with my approach:
The order of the arguments. In rails 4, the scope goes before the options.
The options hash should be wrapped, because it isn't the last argument anymore.
So, this code does work:
has_many :children, -> { order 'id asc' }, { dependent: :destroy } do
def persisted
collect { |a| a if a.persisted? }
end
end
References:
https://www.ruby-forum.com/topic/5436931
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many#461-User-a-block-to-extend-your-associations

Rails - Multiple Nested Attributes

I am trying to use a select tag to save multiple nested children in a parent.
This is the error I'm looking at
Couldn't find all UserLocations with IDs (1, 2) (found 0 results, but was looking for 2)
I have the following setup under Rails 4 and Devise:
User
class User < ActiveRecord::Base
has_many :user_locations
accepts_nested_attributes_for :user_locations, :allow_destroy => true
end
UserLocation (locations that the user has)
class UserLocation < ActiveRecord::Base
belongs_to :user
belongs_to :location
end
Location (predefined list of locations the user can choose upon)
class Location < ActiveRecord::Base
has_many :user_locations
has_many :users, through: :user_locations
end
However, when trying to save the selected UserLocations, they won't be saved.
Rails Select Tag (You can choose multiple items)
<%= f.select :user_location_ids, options_for_select(Location.all.collect { |l| [ l.name, l.id ] }, #user.user_locations.collect{ |l| l.id }), {}, { multiple: true } %>
I have put the user_location_ids in my application_controller as user_location_ids: []
Cheers
Solved
The solution is to overwrite the default setter method for multiple nested models model_ids=(value). Do not use the plural of the model, e.g. models_ids=(value), because that is wrong!
def user_location_ids=(value)
for slot in value do
unless slot == ""
location = Location.find_by(id: slot.to_i)
unless location.nil?
self.user_locations << UserLocation.create(user_id: self.id, location_id: location.id)
end
end
end
end