I have tables applications and business.
Applications
has_many :business
Business
belongs_to :applications
If I will create an Application, I should have at least one Business. I used link_to_add in the same form where I create applications.
I used validate_association :applciations but it didn't work.
You may be better to use validates_associated:
#app/models/business.rb
class Business < ActiveRecord::Base
belongs_to :application
validates :name, :other, param, presence: true
end
#app/models/application.rb
class Application < ActiveRecord::Base
has_many :businesses
validates_associated :businesses
end
This gives you the ability to check the validity of any associated data you pass through the model. However, this will not determine if there is at least one associated business passed through your model.
--
Numerical Validation
You'll want to look at the following
The reject_if method in Rails will not give you the ability to check the number of associated items which have been sent. This will have to be custom coded, which the author of the above post has addressed (in 2012) by setting a custom constant:
#app/models/company.rb
class Company < ActiveRecord::Base
OFFICES_COUNT_MIN = 1
validates :name, presence: true
validate do
check_offices_number
end
has_many :offices
accepts_nested_attributes_for :offices, allow_destroy: true
private
def offices_count_valid?
offices.count >= OFFICES_COUNT_MIN
end
def check_offices_number
unless offices_count_valid?
errors.add(:base, :offices_too_short, :count => OFFICES_COUNT_MIN)
end
end
end
I have not tested this myself, but to explain how it works, you'll basically have a custom validator, which allows you to check whether the number of associated objects is less than or equal to the CONSTANT you assign in the class.
You can, of course, achieve this without a constant, but the above example should demonstrate how you're able to create the functionality where at least 1 associated item should be sent
Not sure if this is what you meant but you could do the following in your models e.g.
has_many :businesses, dependent: :destroy
validates :title, presence: true
and in the other model:
belongs_to :application
validates :name, presence: true
You can use validates_presence_of
Validates that the specified attributes are not blank (as defined by Object#blank?), and, if the attribute is an association, that the associated object is not marked for destruction. Happens by default on save.
Example:
Applications
has_many :businesses
validates_presence_of :business
Business
belongs_to :applications
Update:
I think you will be better to using accepts_nested_attributes_for
Nested attributes allow you to save attributes on associated records through the parent. By default nested attribute updating is turned off and you can enable it using the accepts_nested_attributes_for class method. When you enable nested attributes an attribute writer is defined on the model.
app/models/application.rb
Class Application < ActiveRecord::Base
has_many :businesses
accepts_nested_attributes_for :businesses, reject_if: :all_blank
end
#app/models/business.rb
Class Business < ActiveRecord::Base
belongs_to :application
end
This will give you the ability to call the reject_if: :all_blank method -
:reject_if
Allows you to specify a Proc or a Symbol pointing to a method that checks whether a record should be built for a certain attribute hash. The hash is passed to the supplied Proc or the method and it should return either true or false. When no :reject_if is specified, a record will be built for all attribute hashes that do not have a _destroy value that evaluates to true. Passing :all_blank instead of a Proc will create a proc that will reject a record where all the attributes are blank excluding any value for _destroy.
Related
Here are two models
Product and Variant
class Product.rb
has_many :variants, dependent: :destroy
end
class Variant.rb
belongs_to :product
end
Variant.create(product_id:rand(500..3000))
The above line creates a variant in the db even if product is not present with that particular id.
To handle this,
in before_create I could query and check if product is present.
Other than this is there any in-built method or function that rails provides to handle such cases.
It should be like below in variant.rb
class Variant < ApplicationRecord
belongs_to :product
end
In Rails 5, whenever we define a belongs_to association, it is required to have the associated record present by default after this change.
In Rails 4.x world To add validation on belongs_to association, we need to add option required: true .
class Variant < ApplicationRecord
belongs_to :product, required: true
end
Try:
class Variant.rb
belongs_to :product
validates :product, presence: true
end
According to the docs, this will ensure the product record actually exists.
BTW, it should be belongs_to :product, not belongs_to :products.
In Rails 5 by default the reference is required when you specify a belongs_to relation. You need to specify optional: true in case you do not want any compulsion on reference presence.
In Rails 4, you need to specify required: true to ensure the reference object is present and valid.
belongs_to :product, required: true
You can checkout the blogpost below:
https://blog.bigbinary.com/2016/02/15/rails-5-makes-belong-to-association-required-by-default.html
Hope this helped.
Is there any reason you would use reject_if and do something like this?
class User < ActiveRecord::Base
has_many :posts
accepts_nested_attributes_for :posts, reject_if: proc do |attributes|
attributes['title'].blank?
end
end
Instead of using validation on the Post model?
class Post < ActiveRecord::Base
belongs_to :user
validates :title, presence: true
end
If you use validation, the creation of User will fail if there exists a Post which does not have a title.
If you use reject_if, the creation of User will succeed even if some, or all, of the Posts don't have titles. Only those posts without titles won't be saved.
If you want to go straight to the question, just go to the last paragraph.
A Pack has many items included, Item is polymorphic and one of the linked tables is Access (so Access is an Item that can be added to the Pack)
Here you are the models and controller.
class Pack < ActiveRecord::Base
has_many :pack_items, dependent: :destroy
has_many :items_included, through: :pack_items, source: :item
accepts_nested_attributes_for :pack_items, allow_destroy: true
validate :valid_max_value, if: :infinite_item?
end
class Item < ActiveRecord::Base
has_many :pack_items, dependent: :restrict_with_error
has_many :packs, through: :pack_items
end
class Access < ActiveRecord::Base
has_one :item, as: :itemable, dependent: :destroy
has_one :entitlement, as: :entitlementable, dependent: :destroy
accepts_nested_attributes_for :item, allow_destroy: true
accepts_nested_attributes_for :entitlement, allow_destroy: true
validate :valid_max_value, if: :infinite?
private
def infinite?
entitlement.infinite
end
end
class PacksController < BaseController
def update
#pack = Pack.find(params[:id])
if #pack.update(permitted_params)
...
end
end
private
def permitted_params
params.require(:pack).permit(item_attributes: [:id, :name, :max_purchasable],
pack_items_attributes: [:id, :item_id, :amount, :_destroy])
end
end
There is an importan validation in pack "valid_max_value. If a pack has an infinite Access inside, the max_value of the Pack should never be higher than 1.
It works perfectly when I create a pack and I add some Accesses, but the problem is this:
I have a Pack with two Items. An Access that's infinite and a common Access(not infinite). So the Pack's max_value should be 1 because it has an infinite Access inside.
Now I edit that Pack and I delete the infinite Access, so now I can select a higher max_value, 5 in example, because the pack doesn't have an Access with restriction inside.
When I click update there is a rollback because the valid_max_value validation runs before the deletion of the infinite Access, so it says the max_value is invalid because the validation depends on a child field.
In short, my question is: How can I delete the nested items before run the parent validation?
You do not have to actually delete the items before validation, check marked_for_destruction? instead in your validations, so that items that are to be deleted will be ignored
I was reading validation from http://guides.rubyonrails.org/active_record_validations.html . I understand that presence checks whether given attribute is either empty or consists of whitespace. But what I don't understand is that, how to test association is present. They showed two example
class LineItem < ActiveRecord::Base
belongs_to :order
validates :order, presence: true
end
and
class Order < ActiveRecord::Base
has_many :line_items, inverse_of: :order
end
I understand the code here but I don't understand how it test association.
LineItem has order_id and that is what being validated. it is simply checking if that field/column has a value.
Looking at the documentation on associations, I've managed to set up my classes to use has_many, :through. However, I can't seem to find any example on how to actually use the association.
My User model has_many :attendees and has_many :events, through: :attendees. My Event model has_many :attendees and has_many :users, through: :attendees.
Attendee model:
class Attendee < ActiveRecord::Base
attr_accessible :status
validates_inclusion_of :status, in: [:performing, :invited, :going, :maybe]
belongs_to :user
belongs_to :event
def status
read_attribute(:status).to_sym
end
def status=(value)
write_attribute(:status, value.to_s)
end
end
I tried using the following code:
at1 = Attendee.new(user: u1, event: e1)
at1.status = :invited
at1.save
Unsurprisingly, I get a mass assignment error with user and event. It seems besides the point to declare attr_accesible for user and event though. how would I use the association here, and set the custom status attribute?
It's absolutely not beside the point to declare attr_accessible. That's what you're missing.
Keep in mind attr_accessor is something else. For more on this, check: Difference between attr_accessor and attr_accessible
Also keep in mind that attr_accessible has been deprecated in Rails 4 in favor of Strong Parameters, effectively moving all whitelisting of attributes from the models to the controllers.