I have two models:
class Shift < ActiveRecord::Base
attr_accessible :ranges_attributes
has_many :ranges
accepts_nested_attributes_for :ranges, allow_destroy: true
end
class Range < ActiveRecord::Base
belongs_to :shift
validates :shift, presence: true
end
When, in my controller, I want to create a shift with ranges I'm getting:
Shift.create! params[:shift]
#ActiveRecord::RecordInvalid Exception: Validation failed: Shift ranges shift can't be blank
If I remove validates :shift, presence: true from Range model this works beautifully. I'm able to create a new shift with his children. ActiveRecord does that for me.
The question is: why do I need to remove that validation to make this work?
The thing with validating presence of parent like this is timing !! actually the Shift is not yet saved so when trying to create nested ranges it won't find parent Shift in database.
I found this workaround here
class Shift < ActiveRecord::Base
attr_accessible :ranges_attributes
has_many :ranges, :inverse_of => :shift
accepts_nested_attributes_for :ranges, allow_destroy: true
end
and i quote (with minor modifications) from the same source:
With this option rails won't try to get parent from database when
child is validated. The parent will be got from memory. If you don't
familiar with this option I strongly recommend you to read an official
rails guide
Related
Application
I am working on a college admissions system where a student can make an application to up to 5 courses. The way I have designed this is to have an Application model and a CourseApplication model. An application can consist of many course_applications:
class Application < ActiveRecord::Base
# Assosciations
belongs_to :user
has_many :course_applications, dependent: :destroy
has_many :courses, through: :course_applications
has_one :reference
# Validations
validates :course_applications, presence: true
end
Course Application
class CourseApplication < ActiveRecord::Base
# Intersection entity between course and application.
# Represents an application to a particular course, has an optional offer
# Associations
belongs_to :application
belongs_to :course
has_one :offer, dependent: :destroy
end
I want to make sure that a student cannot apply to the same course twice. I have already done some research but have had no success. This is the UI for a student making an application:
Screenshot of application form
When a course is selected, the course id is added to an array of course ids:
def application_params
params.require(:application).permit(:user, course_ids: [])
end
Right now a student can select the same course twice, I want to prevent them from doing this. Any help is much appreciated.
For the rails side, I would do on the CourseApplication
validates :course, uniqueness: { scope: :application }
For your reference this can be found at: http://guides.rubyonrails.org/active_record_validations.html#uniqueness
Also suggest on the database side to make a migration
add_index :course_applications, [:course, :application], :unique => true
For the validating on the form you will have to write javascript to make sure two things are selected, this will just return an error when someone tries to do it.
I have the following association:
class Budget < ActiveRecord::Base
has_many :budget_components
end
class BudgetComponent < ActiveRecord::Base
belongs_to :budget
end
when I save budget's collection of budget_component objects a field (we'll call it revenute_precentage) in the budget_component needs to be validated such that all of thebudget'sbudget_component'srevenue_percentagefields add up to1.0` (e.g. that all must equal 100% together).
The problem I'm anticipating to happen is that the database might validate one at time or compare the one unsaved record with the saved records and what should be valid, may be invalidated.
Should I add a validator on the budget object that validates is budget_components or will this just work?
I'd personally add a validator to the BudgetComponent model, and pass data through accepts_nested_attributes_for, like this:
class Budget < ActiveRecord::Base
has_many :budget_components
accepts_nested_attributes_for :budget_components
end
class BudgetComponent < ActiveRecord::Base
belongs_to :budget
validates :your, :attributes,
presence: { message: "Can't be blank!" }
end
This will mean when you pass your params from your form to your model, it will validate each member of the collection individually
You'll have to use nested forms to get this working correctly
I have a model Purchase with:
class Purchase < ActiveRecord::Base
has_many :purchase_items, dependent: :destroy
accepts_nested_attributes_for :purchase_items, reject_if: :all_blank, allow_destroy: true
validates_length_of :purchase_items, minimum: 1
end
And PurchaseItem with:
class PurchaseItem < ActiveRecord::Base
belongs_to :purchase
end
Say I have a purchase with only one item. If I mark the item for destruction by doing:
purchase.purchase_items.first.mark_for_destruction
purchase.save!
The purchase is saved fine, leaving it without any referenced items in the DB.
Inspecting the validate_each method inside ActiveModel::Validations::LengthValidator, we can see that it does not verify if the value being validated has objects marked for destruction.
https://github.com/rails/rails/blob/master/activemodel/lib/active_model/validations/length.rb
Is that the normal behavior or it is in fact an issue? If it is normal, what is the correct way to validate the length of a relation along with marked_for_destruction objects?
(without custom validators of course...)
Your only choice is to write a custom validator.
This is an open bug, and there is a PR to fix it, but they've just kinda been sitting there for months:
https://github.com/rails/rails/issues/7247
https://github.com/rails/rails/pull/9917
The situation:
class Cellar < ActiveRecord::Base
belongs_to :house, dependent: :destroy
accepts_nested_attributes_for :house, allow_destroy: true
attr_accessible :house_id, :house_attributes, [...]
end
.
class House < ActiveRecord::Base
has_one: cellar
end
The problem:
When I send the Cellar form and include the key-value pair "_destroy" => "true" inside the house_attributes, the House gets destroyed as it should, but the Cellar.house_id is not updated to NULL.
Is this normal behavior? How should I best fix this?
This may be normal depending on the version of Rails... I think up until Rails 3.2 it was normal for the foreign key to remain untouched when destroying object (and vice versa when updating a foreign key to nil). What version of Rails are you using?
But, regardless, if you want to keep going as is and just clean up the foreign key on #cellar after saving then you can always just call #cellar.reload after the successful #cellar.save. This would refresh the state of your #cellar object from the database and remove the house_id attribute since it's no longer present.
For the sake of completeness, here's what I ended up doing in order to correct the issue:
class Cellar < ActiveRecord::Base
belongs_to :house, dependent: :destroy
accepts_nested_attributes_for :house, allow_destroy: true
attr_accessible :house_id, :house_attributes, [...]
# I ADDED THIS:
before_save :drop_invalid_id
def drop_invalid_id
self.house_id = nil if house.marked_for_destruction?
end
end
I've been struggling with this for some time now. I'm just trying to get nested attributes to validate on Rails 3.2 with no luck. It's like it's just completely ignoring validations for the nested attributes. Below is an example validation that's not working:
class Invoice < ActiveRecord::Base
validates :description, :presence => true
belongs_to :client_branch
has_many :invoice_items
accepts_nested_attributes_for :invoice_items, :allow_destroy => true
end
class InvoiceItem < ActiveRecord::Base
belongs_to :invoice
validate :thisisatest
def thisisatest
errors.add(:qty, 'QTY NOT VALIDATING TEST.')
end
end
When saving an Invoice with some InvoiceItems, it saves it successfully, even though the custom validation is clearly adding an error for the :qty attribute. Is there something I should be adding to my models for nested validation to work, or am I perhaps missing something else?
Actually, I'm being daft. I changed the model name, along with all references to it and it took me this long to miss one reference still pointing to the old model in the javascript. Thus, items being added dynamically weren't named correctly, causing the validation not to trigger. :/