I don't know why, but for some reason my nested association is not being respected by rails.
My student class has nested associations: addresses, tests
The addresses association works fine. The tests association, which is identical to the addresses association, does not work. While I can create and edit test instances successfully, they do not undergo the validation checks defined in their model, nor do they implement the ransack search features that I specified in their model.
Here's the student model:
class Student < ActiveRecord::Base
unloadable
validates :name, presence: true, length: {maximum: 50}
has_many :addresses, :dependent => :destroy
has_many :tests, :dependent => :destroy
accepts_nested_attributes_for :addresses,
:reject_if => :all_blank,
:allow_destroy => true
accepts_nested_attributes_for :tests,
:reject_if => :all_blank,
:allow_destroy => true
end
Here's the addresses model (which works):
class Address < ActiveRecord::Base
belongs_to :student
validates :address, presence: true, length: {maximum: 80}
def self.ransackable_attributes(auth_object = nil)
['county', 'zip', 'address']
end
end
And here's the (basically) identical tests model (which doesn't work):
class Test < ActiveRecord::Base
belongs_to :student
validates :name, length: {maximum: 40}
def self.ransackable_attributes(auth_object = nil)
['score', 'date']
end
end
It's completely ignoring the validation as well as the ransack function. While everything runs without error, I am able to input whatever I want, even if it's well over validated length, and it throws no error message.
I feel like I didn't create my model correctly or something, and Rails doesn't know it's there?
Test, I believe is a module that in the minitest framework. Avoid naming your models Test. As a matter of fact test is a reserved keyword in Rails. Try renaming your model's name and trying again.
Related
This is a frustratingly simple problem, but it has vexed me.
I have two models, comments and notes. A comment can have one or no notes. Notes have only a text field. They have a shared form with an accepts_nested_attributes_for field.
At the moment, every time a comment is made, it creates an associated blank note. I only want the note to be created if something is entered in the note's text field. I don't want a gagillion blank notes.
I suspect this is a trivial problem, but I am damned if I can solve it.
I tried validates :text, presence: true, on note but, when it fails, it prevents the parent comment from being created, which is not what is wanted. Grrr.
note.rb
class Note < ApplicationRecord
belongs_to :comment
validates :text, presence: true
comment.rb
class Comment < ApplicationRecord
...
has_one :note, dependent: :destroy
accepts_nested_attributes_for :note
comment/_form.html.erb
<%= form.fields_for :note do |note_form| %>
Notes<br />
<%= note_form.text_area :text, cols: 57, rows: 8 %>
<% end %>`
It is, I guess, doing what it is supposed to do. I just don't want it to do that...
A virtual pint to anyone who can help.
New answer.
I had a look at your issue and turns out that accepts_nested_attributes_for [NOTE the link is for rails 6, but its same with rails 5] expects additional parameters. One of which is reject_if, that means you can pass a proc and if it returns false, the nested record will not save.
So, in your case, you can do the followings
# app/models/note.rb
class Note < ApplicationRecord
belongs_to :comment, optional: true
end
# app/models/comment.rb
class Comment < ApplicationRecord
has_one :note, dependent: :destroy
accepts_nested_attributes_for :note, reject_if: proc { |attributes| attributes['text'].blank? }
end
Note that,
accepts_nested_attributes_for :note, reject_if: proc { |attributes| attributes['text'].blank? }
which will return false if the text on note is blank and in turn avoid saving the blank note record.
I've had a quick test and this works in my rails 5.2 app. \o/
You can refer my bare minimum rails app on github if need be sameera207/so-question-58599317
Initial answer, doesn't work
Assuming this is rails 5, Try :inverse_of
UPDATE: optional: true added after the comment by the OP
class Note < ApplicationRecord
belongs_to :comment, inverse_of: :notes, , optional: true
validates :text, presence: true
class Comment < ApplicationRecord
...
has_one :note, dependent: :destroy, inverse_of: :comment
accepts_nested_attributes_for :note
This is a very good writeup about :inverse_of and it talks about accepts_nested_attributes_for, which I think the issue you are having
In my Rails 5 app I have the following setup:
class Client < ApplicationRecord
has_one :address, :as => :addressable, :dependent => :destroy
accepts_nested_attributes_for :address, :allow_destroy => true
end
class Company < Client
has_many :people
end
class Person < Client
belongs_to :company
end
class Address < ApplicationRecord
belongs_to :addressable, :polymorphic => true
validates :city, :presence => true
validates :postal_code, :presence => true
end
A person can belong to a company but doesn't necessarily have to.
Now I want to validate a person's address only if that person doesn't belong to a company. How can this be done?
There might be other approaches as well, but based on my experience, something like this should work.
validates :address, :presence => true, if: -> {!company}
Hope this helps.
Validations can take either an if or unless argument, which accept a method, proc or string to determine whether or not to run the validation.
In your case:
validates :address, presence: true, unless: :company
Update according to comments
The above only takes care of skipping the validation itself, but due to accepts_nested_attributes_for OP still saw errors when trying to persist a missing address. This solved it:
accepts_nested_attributes_for :address, reject_if: :company_id
Nabin's answer is good but wanted to show another way.
validate :address_is_present_if_no_company
def address_is_present_if_no_company
return if !company_id || address
errors.add(:address, "is blank")
end
Say we've got the following models:
class User < ActiveRecord::Base
has_many :widgets
accepts_nested_attributes_for :widgets, allow_destroy: true
end
class Widget < ActiveRecord::Base
belongs_to :user
validates :title, presence: true, uniqueness: { scope: [:user_id] }
end
When I save a user with nested widget attributes that contain a duplicate title I get a validation error as expected. What's a good way to avoid the validation error and silently eliminate the duplicate entries before saving?
Thanks.
You could just reject the nested attributes if they don't match certain criteria:
accepts_nested_attributes_for :widgets,
allow_destroy: true,
reject_if: lambda { |w| Widget.pluck(:title).include?(w.title) && Widget.pluck(:user_id).include?(w.user_id) }
I have got three models
class RateCard < ActiveRecord::Base
validate :name, :presence => true, :uniqueness => true
has_many :rate_card_countries, :dependent => :destroy
has_many :rate_card_details, :dependent => :destroy
has_many :countries, :through => :rate_card_countries
end
class RateCardCountry < ActiveRecord::Base
validates :country_id, :presence => true, :uniqueness => true
validates :rate_card_id, :presence => true
belongs_to :rate_card
belongs_to :country
end
class Country < Geography
has_one :rate_card
has_one :rate_card_country
end
In rate_cards_controller i want to create/update rate_cards such that one country should have one rate_card..
For that i have added uniqueness validation in RateCardCountry Model.
And NOw i want to display the error in rate_card_controller while creating/updating rate_cards..
Needed help?
If I understand your intention correctly, you are trying to build a one-to-many relationship between RateCard and Country. In other words- a country will have only one RateCard, and a RateCard can belong to many countries.
Assuming that's the case, you really don't need the RateCardCountry model (which will be useful if you wanted it to be a many-to-many relationship).
You will need to have:
class RateCard < ActiveRecord::Base
validate :name, :presence => true, :uniqueness => true
belongs_to :rate_card
end
And make sure you have county_id foreign key in the RateCard table.
and then:
class Country < ActiveRecord::Base
has_one :rate_card
end
Also, it seems that right now you have:
class Country < Geography
I am not sure if you are subclassing from a Geography class, as you have not provided the rest of the code.
Hope that helps.
I am using Ruby on Rails v3.2.2. I would like to solve the issue related to the validation of a foreign key when using accepts_nested_attributes_for and validates_associated RoR methods. That is, I have following model classes:
class Article < ActiveRecord::Base
has_many :category_associations, :foreign_key => 'category_id'
accepts_nested_attributes_for :category_associations, :reject_if => lambda { |attributes| attributes[:category_id].blank? }
validates_associated :category_associations
end
class CategoryAssociation < ActiveRecord::Base
belongs_to :article, :foreign_key => 'article_id'
belongs_to :category, :foreign_key => 'category_id'
validates :article_id, :presence => true
validates :category_id, :presence => true
end
... and I have following controller actions:
class ArticlesController < ApplicationController
def new
#article = Article.new
5.times { #article.category_associations.build }
# ...
end
def create
#article = Article.new(params[:article])
if #article.save
# ...
else
# ...
end
end
end
With the above code ("inspired" by the Nested Model Form Part 1 Rails Cast) my intent is to store category associations when creating an article (note: category objects are already present in the database; in my case, I would like just storing-creating category associations). However, when I submit the related form from the related view file, I get the following error (I am logging error messages):
{:"category_associations.article_id"=>["can't be blank"], :category_associations=>["is invalid"]}
Why it happens since validates_associated seems to run the method article.category_association.valid? but only if the article.category_association.article_id is not nil? How can I solve the problem with the presence validation of the article_id foreign key?
However, if I comment out the validates :article_id, :presence => true in the CategoryAssociation model class, it works as expected but it seems to be not a right approach to do not validate foreign keys.
If I comment out the validates_associated :category_associations in the Article model class, I still get the error:
{:"category_associations.article_id"=>["can't be blank"]}
Use inverse_of to link the associations and then validate the presence of the associated object, not the presence of the actual foreign key.
Example from the docs:
class Member < ActiveRecord::Base
has_many :posts, inverse_of: :member
accepts_nested_attributes_for :posts
end
class Post < ActiveRecord::Base
belongs_to :member, inverse_of: :posts
validates_presence_of :member
end
Since you have a possible nested form with accepts_nested_attributes_for, therefore in CategoryAssociation you need to make the validation conditional, requiring presence for only for only updates:
validates :article_id, presence: true, on: :update
Aside from Active Record associations, you should have a foreign key constraints at the db level.
If you're stucked with this kind of errors too, try to replace:
validates :article_id, :presence => true
validates :category_id, :presence => true
with:
validates :article, :presence => true
validates :category, :presence => true
worked for me.
Validations will run on create or save (as you'd expect), so ask yourself, "at each one of those is there a saved instance being referred to?", because without a save an instance won't have an id as it's the database that assigns the id.
Edit: Like I've said in the comments, if you're going to downvote then leave a comment as to why.