Rails: use REJECT_IF only on CREATE action for nested attributes - ruby-on-rails

So I have many polymorphic children for a Profile object. The models are designed to destroy after_save if the specific Object's field is blank.
But for accept_nested_attributes I don't want to create the child object in the first place if it's blank. But if I leave the reject_if statement then the user no longer has the ability to empty the field on UPDATE because the reject_if rejects their blank input.
accepts_nested_attributes_for :socials, reject_if: proc { |att| att['username'].blank? }
accepts_nested_attributes_for :phones, reject_if: proc {|att| att['number'].blank? }
accepts_nested_attributes_for :websites, reject_if: proc {|att| att['url'].blank? }
So I want to reject_if: { ... }, on: :create. But that doesn't seem to work.

You can create a method and instead of sending a proc into the reject_if option, which is the same, it is just more readable, so the code would look like:
accepts_nested_attributes_for :socials, reject_if: :social_rejectable?
private
def social_rejectable?(att)
att['username'].blank? && new_record?
end
You can just repeat the methods and then clean it up with some metaprogramming or add the new_record? method on the proc
accepts_nested_attributes_for :socials, reject_if: proc { |att| att['username'].blank? && new_record?}

Related

Is there an easy way to remove blank objects from a has-many association?

I got the following models:
class Course < ApplicationRecord
has_many :external_ids, as: :identifiable, dependent: :destroy, validate: true
accepts_nested_attributes_for :external_ids
end
and this one:
class ExternalId < ApplicationRecord
belongs_to :identifiable, polymorphic: true
validates :name, presence: true, uniqueness: true
validates :identifiable, presence: true, on: :create
end
Because of a nested form in the course form-view it's possible that a blank object of an external id will be saved. I'd like to remove these blank objects. With "blank object" I mean an ExternalId which is a new record and has a blank name. Currently I do that as follows:
#course.attributes = course_params
## SNIP - workaround to fix validations
external_ids = []
#course.external_ids.each do |ext_id|
external_ids << ext_id unless(ext_id.new_record? && ext_id.name.blank?)
end
#course.external_ids = external_ids
## SNAP - workaround to fix validations
#course.save
But this is a lot of code for a very simple task. Is there any function for that? destroy_if doesn't exists for an association, only for arrays.
Thanks you!
You can use accepts_nested_attributes_for with such key
accepts_nested_attributes_for :external_ids, reject_if: proc { |attributes| attributes['name'].blank? }
From docs:
You may also set a :reject_if proc to silently ignore any new record hashes if they fail to pass your criteria

Rails 5 optional has_many associations

app/models/donor.rb
has_many :donor_relationships
accepts_nested_attributes_for :donor_relationships, :allow_destroy => true
app/models/donor_relationship.rb
belongs_to :donor, optional: true
I am using f.fields_for in donor form and creating donor and donor_relationships both.
donor_relationships is not required must. Issue I face is that when if I not add any donor_relationships then empty record of donor_relationship is created with donor id. In rails 4 not like this happen.
How can I fix this?
accepts_nested_attributes_for ignore blank values
You can add a reject_if conditional to the accepts_nested_attributes method. Assuming your donor_relationship has an attribute of name (you can go with relationship_id or whatever attribute makes sense):
accepts_nested_attributes_for :donor_relationships,
:allow_destroy => true,
:reject_if => lambda { |c| c[:name].blank? }`
You can use reject_if option:
accepts_nested_attributes_for :donor_relationships,
allow_destroy: true,
reject_if: proc { |attributes| attributes['important_field'].blank? }

Rails 4 how to check if nested attributes _destroy flag is set from within model

I have model (container) that accepts nested attributes (including allow_destroy) for a has_one relationship to another model (reuse_request). There is a before_validation callback in the container model that I don't want to run if the the reuse_request is about to be destroyed in the same update.
Is there a way to check if the _destroy attribute has been passed from within the container model before_validation callback?
#container.rb
before_validation :set_code
has_one :reuse_request_as_previous, class_name: 'ReuseRequest', foreign_key: 'previous_container_id', dependent: :destroy, inverse_of: :previous_container
accepts_nested_attributes_for :reuse_request_as_new, :allow_destroy => true
def set_code
if reuse_request_as_new && reuse_request_as_new.previous_container
self.code = reuse_request_as_new.previous_container.code
end
end
How about using .marked_for_destruction??
def set_code
return if reuse_request_as_new.marked_for_destruction?
...
end

Modify Nested attribute array before_save

I am trying to modify nested attributes before they are saved in my database. The idea is that if someone has already submitted a costume with a given cid in the costume database, we should pull that one and use it for the current agreement. However, through debugging, I've found that the nested attributes array doesn't change at all. Any thoughts?
Thanks!
Matt
app/models/agreement.rb
class Agreement < ActiveRecord::Base
before_save :get_costumes
has_and_belongs_to_many :costumes, join_table: :agreement_costumes
accepts_nested_attributes_for :costumes, reject_if: proc { |attributes| attributes['cid'].blank? }
has_many :drycleans
accepts_nested_attributes_for :drycleans, allow_destroy: true, reject_if: :all_blank
def get_costumes
self.costumes.map! { |costume|
unless Costume.where(cid: costume.cid).nil?
Costume.where(cid: costume.cid).first
end
}
end
end
Your unless-condition is never true. With a where-statement you get always an ActiveRecord. And also a empty ActiveRecord is not nil.
Try to change that condition to:
unless Costume.where(cid: costume.cid).count == 0
Or to:
unless Costume.find_by_cid(costume.cid).nil?

has_many association, nested models and callbacks

I've got model A and model Attach. I'm editing my A form with nested attributes for :attaches. And when I am deleting all attaches from A via accepts_nested_attributes_for how can I get after_update/after_save callbacks for all of my nested models? Problem is that when I am executing callbacks in model A they are executed right AFTER model A is updated and BEFORE model Attach is updated, so I can't, for example, know if there is NO ANY attaches after I delete them all :).
Look for example: my callback after_save :update_status won't work properly after I delete all of my attaches.
model A
after_save :update_status
has_many :attaches
accepts_nested_attributes_for :attaches, :reject_if => proc { |attributes| attributes['file'].blank? }, :allow_destroy => true
def update_status
print "\n\nOUPS! bag is empty!\n\n" if self.attaches.empty?
end
end
model Attach
belongs_to A
end
I am using rails 3 beta
From rubyonrails.org:
IMPORTANT: In order for inheritance to work for the callback queues, you
must specify the callbacks before
specifying the associations.
Otherwise, you might trigger the
loading of a child before the parent
has registered the callbacks and they
won‘t be inherited.
Isn't it your problem? You're specifying the association before the callback.
Ok, I've removed after_save callback from A to nested model Attach (after_destroy callback)
model A
has_many :attaches
accepts_nested_attributes_for :attaches, :reject_if => proc { |attributes| attributes['file'].blank? }, :allow_destroy => true
end
model Attach
after_destroy :update_status
belongs_to :a
def update_status
print "\n\nOUPS! bag is empty!\n\n" if self.a.attaches.empty?
end
end

Resources