I'm using accepts_nested_attributes_for with :allow_destroy => true.
When deleting an object, I can see that the attribute _destroy is marked as true, but when I'm checking my object with object.destroyed?, I'm getting nil instead of true.
Any ideas why?
From the doc:
Now, when you add the _destroy key to the attributes hash, with a value that evaluates to true, you will destroy the associated model:
member.avatar_attributes = { :id => '2', :_destroy => '1' }
member.avatar.marked_for_destruction? # => true
destroyed? is here to check if the object is actually destroyed:
foo = Foo.first
foo.destroyed #=> false
foo.destroy
foo.destroyed? #=> true
Related
How can I skip validation for nested_attribute if condition is true
aquarium.rb
has_many :fishes
accepts_nested_attributes_for :fishes,
fish.rb
belongs_to :aquarium
validates :ratio, :numericality => { :greater_than => 0 }, if: :skip_this_validation
then in aquariums_controller.rb
def some_action
#aquarium = Aquarium.new(aqua_params)
#aquarium.skip_this_validation = true # i know that this is not valid
#must skip validation for ratio and then save to DB
end
aquarium.rb
has_many :fishes
accepts_nested_attributes_for :fishes,
attr_accessor :skip_fishes_ratio_validation
fish.rb
belongs_to :aquarium
validates :ratio, :numericality => { :greater_than => 0 }, unless: proc { |f| f.aquarium&.skip_fishes_ratio_validation }
then in aquariums_controller.rb
def some_action
#aquarium = Aquarium.new(aqua_params)
#aquarium.skip_fishes_ratio_validation = true
#aquarium.save
end
You can just add the condition in method and check for the conditional validation
class Fish < ActiveRecord::Base
validates :ratio, :numericality => { :greater_than => 0 }, if: :custom_validation
private
def custom_validation
# some_condition_here
true
end
end
#aquarium.save(validate: false)
I believe skips validations on the model.
In Rails 5 you can simply pass optional: true to the belongs_to association.
So in your fish.rb, update association with the following code:
belongs_to :aquarium, optional: true
It will skip association validation if the fish object has no aquarium.
rails 3.2
In my tickets_controller, I have the following:
def update
#ticket = Ticket.find params[:id]
authorize! :update, #ticket
#ticket.assign_attributes(params[:ticket])
#ticket.customer_info.company = #ticket.customer if #ticket.customer_info
#ticket.admin_context = true
if !params[:ticket_update_type].nil? && params[:ticket_update_type] == 'save_lead_billing'
#ticket.process_lead_billing params
end
if #ticket.save
#ticket.update_attribute(:ticket_type, #ticket.ticket_profile.ticket_type)
redirect_to [:admin, #ticket], notice: success_message
else
#ticket.customer_info_type = 'existing'
#can_update_ticket = can? :update, #ticket
#tag_groups = TagGroup.with_type('ticket').for_company(#ticket.customer_id)
flash.now[:error] = #ticket.errors.full_messages.join(', ')
render action: "show"
end
end
In my ticket.rb model, I have the following:
def process_lead_billing params
if params[:lead_billing]["pre_tax_total"].nil? || params[:lead_billing]["post_tax_total"].nil?
return
end
# handles case where billing infor has not been added to lead ticket
if params[:ticket_update_type] == "save_lead_billing"
lead_billing = LeadBilling.new(
:ticket_id => self.id,
:pre_tax_total => params[:lead_billing]["pre_tax_total"],
:post_tax_total => params[:lead_billing]["post_tax_total"],
:status => 'entered'
)
lead_billing.save!
end
end
And in lead_billing.rb model, I have the following:
class LeadBilling < ActiveRecord::Base
validates_presence_of :pre_tax_total, :post_tax_total
validates_numericality_of :pre_tax_total, greater_than: 0, allow_blank: false, only_integer: false
validates_numericality_of :post_tax_total, greater_than_or_equal_to: :pre_tax_total, allow_blank: false, only_integer: false
The problem, is that when the form is submitted, with the pre_tax_total and post_tax_total empty, I am getting an error message.
From the log file:
Started PUT "/admin/tickets/163812" for 73.83.66.151 at 2016-12-21 22:05:28 +0000
Processing by Admin::TicketsController#update as HTML
and the params are:
[utf8] => ✓
[authenticity_token] => mNt+aI3YInoutup4UsBGZ8zZkeFRYCBZAsxEv4JPvoE=
[ticket] => Array
(
......
)
[time_span] =>
[city] => Draper
[state] => Utah
[admin] => true
[specialty] =>
[services] =>
[inventories] =>
[ticket_update_type] => save_lead_billing
[ticket_id] => 1480720184_0388234_ticket
[lead_billing] => Array
(
[pre_tax_total] =>
[post_tax_total] =>
)
[id] => 163812
From the log file, the error is:
Validation failed: Pre tax total can't be blank, Pre tax total is not
a number, Post tax total can't be blank, Post tax total is not a number from
And then it points me to the line in the tickets_controller.rb, where the processing_lead_billing method is called (correctly), and then to the line in the processing_lead_billing method, where it tries to save the lead billing.
Execution should have halted, when I checked for nil, but it continued to execute. Any ideas?
Rails has a blank? method that is usually preferred when testing form params.
nil? is a ruby method that will only return true if the object is nil, while blank? covers all sorts of use cases (empty string, empty array, empty dictionary and of course nil value).
In your case, the returned value are most likely empty string given how rails work, and so you should test for blank? instead of nil?.
I have model LoanPlan and Career, they are associated by a join_table
The request params from another frontend developer will be like this
"loan_plan" => {
"id" => 32,
"careers" => [
[0] {
"id" => 8,
},
[1] {
"id" => 9,
}
]
},
However, I got ActiveRecord::AssociationTypeMismatch: Career(#70198754219580) expected, got ActionController::Parameters(#70198701106200) in the update method
def update
#loan_plan.update(loan_plan_params)
end
When I tried to update the loan_plan model with careers params, it expects the params["careers"] should be careers object of a array instead of ids of a array.
So my workround is to manually fectch the careers objects of a array and replace the sanitized params.
It seems dirty and smells bad, any better solution in Rails way? Thanks
def loan_plan_params
# params.fetch(:loan_plan, {})
cleaned_params = params.require(:loan_plan).permit(
:id,
:name,
{:careers=>:id}
)
cleaned_params["careers"] = Career.find(cleaned_params["careers"].map{|t| t["id"]})
cleaned_params
end
model
class LoanPlan < ActiveRecord::Base
has_and_belongs_to_many :careers
accepts_nested_attributes_for :careers
end
In Rails way, params should be
"loan_plan" => {
"id" => "32",
"career_ids" => ["8", "9"]
}
and the strong parameter loan_plan_params should be
def loan_plan_params
params.require(:loan_plan).permit(
:id,
:name,
:career_ids => []
)
end
Some valid ActiveRecord objects return false for present?:
object.nil? # => false
object.valid? # => true
object.present? # => false
object.blank? # => true
I prefer object.present? over not object.nil?. What determines the return value of present?/blank??
EDIT: Found the answer: I had redefined the empty? method on this class, not the blank? or present? method; along the lines of #Simone's answer, blank? is using empty? behind the scenes.
present? is the opposite of blank?. blank? implementation depends on the type of object. Generally speaking, it returns true when the value is empty or like-empty.
You can get an idea looking at the tests for the method:
BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", ' ', "\u00a0", [], {} ]
NOT = [ EmptyFalse.new, Object.new, true, 0, 1, 'a', [nil], { nil => 0 } ]
def test_blank
BLANK.each { |v| assert_equal true, v.blank?, "#{v.inspect} should be blank" }
NOT.each { |v| assert_equal false, v.blank?, "#{v.inspect} should not be blank" }
end
def test_present
BLANK.each { |v| assert_equal false, v.present?, "#{v.inspect} should not be present" }
NOT.each { |v| assert_equal true, v.present?, "#{v.inspect} should be present" }
end
An object can define its own interpretation of blank?. For example
class Foo
def initialize(value)
#value = value
end
def blank?
#value != "foo"
end
end
Foo.new("bar").blank?
# => true
Foo.new("foo").blank?
# => false
If not specified, it will fallback to the closest implementation (e.g. Object).
From the documentation for Object#present?
An object is present if it's not blank.
In your case, object is blank, so present? will return false. Validity of the object does not really matter.
Say I have a hash like so
attributes = {"brown" => true, "black" => false, "rocky" => true, "pebbles" => false, "beige" => true, "white" => false, "red" => true, "pink" => "true" }
And I have an model Beach where I want to query for the properties in the hash
Beach.where(attributes)
I believe this joins the hash attributes with the AND clause brown is true AND black is false AND rocky = true.
I need to join the hash attributes with the OR condition brown is true OR black is false OR rocky is true. Any Ideas? Thank you!