I have a Post model and Comment model (Post has_many comments, Comment belongs_to Post)
I want to validate a field in my Comment model where they can only create/update if a comment field is less than the difference between 2 Post date_fields. How do I go about achieving this?
Right now, in my Comment model, I have:
validates :comment_check, presence: true, :numericality => { :greater_than_or_equal_to => 0 }
I just need to add the validation to make it :less_than_or_equal_to the Post date diff. I also have this model method in my Post model:
def self.days_diff
(end_date.to_date - start_date.to_date).to_i
end
Any help would be greatly appreciated!
In your Comment Model you can validate with:
validate :comment_date
def comment_date
if self.post.end_date < Time.now or self.post.start_date.to_date > Time.now
self.errors.add(:created_at, "failed to save at the moment")
end
end
Related
I have two models Post model and Comment model..First if a create a post it will have a post id of 1 then while creating a comment i can give association to the post using post_id equal to 1 but if i create a comment with post id of 2 which does not exist it would still go ahead and create a comment but with id of 'nil'..I want to ensure that the comment will be created only if the respective post_id is present.
class Post < ActiveRecord::Base
has_many :comments, dependent: destroy
end
class Comment < ActiveRecord::Base
belongs_to :post
validates_associated: post
end
As per my understanding validates_associated checks whether the validations in post model passes before creating a comment. Clarify me if i am wrong and what would be a appropriate solution for the above scenario?
First, the preferred way of setting the association b/w Post-Comment here is by :
def new
#product = Product.first
#comment = #product.comments.build
end
def create
#product = Product.find(params[:comment][:post_id])
#comment = #product.comments.create(comment_params)
end
For your particular scenario, I'm assuming that post_id is coming in params via some form or something, and then you wish to create a comment only if the post with that particular post_id exists. This can be done by adding following in Comment model:
validates :post, presence: true, allow_blank: false
OR
validate :post_presence, on: :create
def post_presence
errors.add(:post_id, "Post doesn't exist") unless Post.find(post_id).present?
end
You can even do the same thing at controller-side with before_action/before_filter hooks.
You can do this to validate the presence of post_id
class Comment < ActiveRecord::Base
belongs_to :post
validates :post_id, :presence => true
end
or to validate association, you can use
class Comment < ActiveRecord::Base
belongs_to :post
validates_presence_of :post
end
I have a couple of classes with a usual one-to-many relationship, like:
class Blog < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :blog
validates :blog_id, presence: true
end
when I build a blog and add to it a bunch of posts:
blog = Blog.new
blog.posts = [Post.new, Post.new]
I cannot just save the blog, like this:
blog.save
because blog_id in the posts is blank, so validation for the posts are failing. I thought Rails was smart enough to either check the id or the presence of the object, but it isn't.
What's the proper Rails way to solve this? Something better than:
saved = true
Blog.transaction do
saved &&= #blog.save
if saved && params[:blog][:posts].responds_to?(:each)
params[:blog][:posts].each do |post|
p = Post.new({blog: #blog}, without_protection: true)
saved &&= p.save
end
end
end
My solution, so far, is to change the validation line from:
validates :blog_id, presence: true
to
validate { errors.add(:blog, "can't be blank") if blog_id.blank? && blog.blank? }
which makes me wonder, why isn't Rails doing that for associations? Is there any danger in doing that?
You can do it two ways
blog.posts.create(attributes_of_new_post)
or
blog.posts << Post.new(attributes_of_new_post)
And change your has_many to be
has_many :posts, :inverse_of => :blog
I am building a simple Ruby on Rails app for problem management. I have a problem model as follows:
class Problem < ActiveRecord::Base
attr_accessible :active, :impact, :incident_number, :issue_description, :root_cause, :user_id, :problem_summary, :incident_priority, :timeline_enabled
attr_accessor :enable_timeline
validates :problem_summary, :length => { :in => 10..100 }
belongs_to :user
has_one :timeline
has_many :actionitems
end
which has a has_many belongs_to association with the model for actionitems:
class Actionitem < ActiveRecord::Base
attr_accessible :completion_date, :description, :initial_due_date, :notes, :problem_id, :revised_due_date, :status, :user_id
belongs_to :problem
end
I would like to be able to update the problem record and save it with some set of limited validations (I still need to add those). However, I would like to have a "Complete problem investigation" button that would trigger a method on the problem controller to set the :active attribute on the problem record to false. I would like to be able to run a different, more complete set of validations on the problem record prior to performing this action and also to validate that all actionitems (if any) that were associated with this problem record are in :status "completed".
The two questions that I have:
How do I perform a specific set of validations only on a given action?
How can I validate that related instances of Actionitem are in status "complete" prior to performing an action on Problem?
This task seems very complex to me. If you could please point me to what I need to utilize in order to be able to achieve this that would be greatly appreciated! (I read on validates :on => :save etc and accepts_nested_attributes_for but I am not sure how to put all of this together to achieve the behavior that I want).
Many thanks for all your help!
try this
validates_length_of :problem_summary, :in => 10..100, :if => :status_active?
def status_active?
self.active == true
end
see in details - validations & validates_length_of
U need to apply checking conditions on validations like
validate :xyz , length => {:in => 1..12}, :if => , :if => lambda {self.active == true }
this validation will only run when aCTIVE IS TRUE. similarly you can add more validation with checking
I have a basic invoice setup with models: Invoice, Item, LineItems.
# invoice.rb
class Invoice < ActiveRecord::Base
has_many :line_items, :dependent => :destroy
validates_presence_of :status
before_save :default_values
def default_values
self.status = 'sent' unless self.status
end
end
# item.rb
class Item < ActiveRecord::Base
has_many :line_items
validates_presence_of :name, :price
end
# line_item.rb
class LineItem < ActiveRecord::Base
belongs_to :item
belongs_to :invoice
before_save :default_values
validates_presence_of :invoice_id
validates :item_id, :presence => true
end
There is more in the model but I only presented the above for simplicity.
I get the following errors:
2 errors prohibited this invoice from being saved:
Line items invoice can't be blank
Status can't be blank
So two problems:
If I remove validates :invoice_id, :presence => true I don't get the Line items invoice can't be blank error message anymore, but why? I do want to validate the invoice_id on line_items, ALL line_items are supposed to have an invoice_id. How can I validate the invoice_id on line_items without getting an error?
Why am I getting the Status can't be blank error if I set it as a default value? I can probably set it up on the invoices_controller but I think the default value should be set in the model, right? How can I validate the presence of status and still have a default value in the model for it?
Both of these validation errors are occurring because validations get called before save (and before the before_save callback).
I'm assuming that you're using a nested_form to create the invoice and it's line items at the same time. If this is the case, you don't want to validates :invoice_id, :presence => true on the line items - the invoice and the line items are coming in at the same time, and the invoice hasn't been saved yet, so it doesn't have an id. If you leave the validation in, you'll need to create and save an empty invoice first, and then create the line items later so the invoice_id is available. If you only want to make sure invoice_id is still set after any edits, you can enforce this via validates :invoice_id, :presence => true, :on => :update this will skip the validation when the line item is being created (and the invoice_id isn't available yet).
You're running into problems with validates :status, :presence => true for similar reasons - the values coming in via the request are being validated against, and the "status" value isn't there. The before_save callback runs after validation. You can set the default value in the before_validation or after_initialization callback and the values will be there when validations are run.
Check out the Callbacks documentation for Rails for more info.
I'll start with 2:
before save is being executed only before save, meaning, after the object passed validation and is about to be saved. If the validation fails - it won't be executed.
as for 1:
Can you give an example of how you're trying to create an invoice?
Problem 1
Try validates_associated which checks that the associated models are all valid
Problem 2
Like most of the answers say before_save gets called after validations. The magic you're looking for is after_initialize which gets run after an object's initialize method is called.
class Invoice < ActiveRecord::Base
after_initialize :default_values
validates :status, presence: true
private
def default_values
self.status ||= 'sent'
end
end
I have a Post has_many Comments association. Post has boolean attribute published.
When post.published is false, the new comment shouldn't be valid.
What is the best practise to accomplish this kind of validation?
I've tried to do it by this way, but sadly, it doesn't work correctly. It is still possible to create new comment for unpublished post.
class Comment < ActiveRecord::Base
validates :post_id, presence: true, if: :post_is_published
...
def post_is_publised
post && post.published
end
end
Hmm.. I think you have syntax errors in your code... Try this:
class Comment < ActiveRecord::Base
validates :post_id, :presence => true, :if => :post_is_published
def post_is_publised
post.try(:published)
end
end
After reading your console output and checking your question one more time:
class Comment < ActiveRecord::Base
validate :post_has_to_be_published
def post_has_to_be_published
unless post.try(:published)
self.errors.add(:base, "you can add comments only to published posts")
end
end
end
I understand that you don't want to allow adding comments to unpublished posts. Above code should accomplish that.