Rails Object in Database is Invalid - ruby-on-rails

I have a model named Tickets that being saved to the database even when
invalid. This is stopping me from using validations to help prevent
duplicate data being saved to the DB. In script/console
>> Ticket.last.valid?
=> False
>> Ticket.first.valid?
=> False
If I try to see what errors are associated with this invalid object
>> Ticket.last.errors.each{|attr,msg| puts "#{attr} - #{msg}\n" }
=> {}
So does anyone know how it's possible to save an invalid object to the
database, and how can I find what is making the object invalid?
Ticket.rb (model)
class Ticket < ActiveRecord::Base
belongs_to :whymail
belongs_to :forms
attr_accessible :to_email, :to_email, :from_email, :subject, :body
validates_uniqueness_of :to_email, :scope => [:body, :from_email]
validates_presence_of :to_email
validates_presence_of :from_email
validates_presence_of :subject
validates_presence_of :body
def after_create
if self.valid?
whymail = Whymail.find(:first, :include => :user, :conditions => ['(email = ?)', self.to_email.upcase ] )
if !whymail.nil?
self.whymail_id = whymail.id
self.save
MyMailer.deliver_forward(whymail.user.email, self.from_email, self.to_email, self.subject, self.body)
end
end
end
end
One part of this question was answered, second was not. Can anyone see problems with this model that may allow it to save even though it is invalid??

It is possible to skip validations. How are you saving it? Is it part of a nested form?
In any case, you should look at the errors like this:
>>t = Ticket.last
>>t.valid?
>>t.errors.each{|attr,msg| puts "#{attr} - #{msg}\n" }
The way you have it above, you are getting a new object with the second Ticket.last call and validation hasn't been run on that one, so you can't see what the errors are.

Try something like:
t = Ticket.last
t.save
puts t.errors.full_messages.inspect
The errors object won't be populated until you try to save the activerecord object.

Related

Uniqueness error in has_many nested attributes

I have a class student with has_many tests. The test class has a student_id, marks, name. Here the test name should be unique. The test is a nested attribute for student. So the parameters are this way:
:student => {:first_name => "abc",
:email => "dfsdf#sfdsdsd.bbb",
:tests_attributes => { "0" => {:name => "bgc", :marks => "470"}}}
I have a problem with update. If I update_attributes with the tests_attributes, it throws a validation error saying the name for test is not unique. I am actually addressing the same record here. How do I overcome this?
Without seeing your models (& validations), it's going to be quite difficult to diagnose your error directly.
--
Nested Attributes
We've done something like this, and found that your nested data is passed to the child model as if it were receiving a new object (without being nested). This means if you've got validates uniqueness for that model, it should be okay:
#app/models/test.rb
Class Test < ActiveRecord::Base
belongs_to :student
validates :name, uniqueness: true
end
Reason I write this is because there's a method called inverse_of, which basically allows you to access the parent model data in your child model
--
Update
I think the problem will likely lie with your use of update_attributes. Problem being you're trying to update both the student and the test attributes at one time.
I'm not sure exactly why this would be a problem, but I'd test this:
#app/controllers/students_controller.rb
class StudentsController < ApplicationController
def update
#student = Student.find params[:id]
#student.test.update(name: params[:test_name], marks: params[:marks])
end
end
I think if you can explain your methodology a little more, it will be much more helpful. I.E are you trying to update student or test? If you're updating student & adding a new test, how are you updating the studet?
Thanks for the reply guys. I ended up finding the answer myself. I did have a uniqueness validation for name.
I had a situation where initially I wouldn't know the student but have only his details. So I would have to create this hash and pass it to update. The trick to not trying to create a new record for the same name in test is to pass the actual record's ID along with it. This solved the problem
Nested Attributes
I think the problem with nested_attributes. For update need to pass nested_attributes with ID.
Ex.
:student => {:first_name => "abc",
:email => "dfsdf#sfdsdsd.bbb",
:tests_attributes => { "0" => {id: 1, :name => "bgc", :marks => "470"}}}
I have tried below-given example it is worked for me:
Update
#app/controllers/students_controller.rb
class StudentsController < ApplicationController
def update
#student = Student.find params[:id]
#student.update_attributes(student_params)
end
private
def student_params
params.require(:student).permit(:first_name, :email,
tests_attributes: [:id, :name, :marks])
end
end

Ruby on Rails : where is stored the error message from the Active record method "validates_uniqueness_of"

how to retrieve the message from a validates_uniqueness_of method that turns out false?
So i can choose to display it in an appropriated place on my view.
thanks for your help
You can access the errors like so:
class Person < ActiveRecord::Base
validates_uniqueness_of :name
end
person = Person.new(:name => "JD")
person.valid? # => false
person.errors[:name] # => ["has already been taken"]
Read about validation here

Mongoid validation uniqueness with scope and belongs_to

I have the following mongoid model, with a scoped validation to prevent multiple votes on one bill. Each vote belongs to a user and a group:
class Vote
include Mongoid::Document
field :value, :type => Symbol # can be :aye, :nay, :abstain
field :type, :type => Symbol # TODO can delete?
belongs_to :user
belongs_to :polco_group
embedded_in :bill
validates_uniqueness_of :value, :scope => [:polco_group_id, :user_id, :type]
end
The user has the following method to add a vote to a bill:
def vote_on(bill, value)
if my_groups = self.polco_groups # test to make sure the user is a member of a group
my_groups.each do |g|
# TODO see if already voted
bill.votes.create(:value => value, :user_id => self.id, :polco_group_id => g.id, :type => g.type)
end
else
raise "no polco_groups for this user" # #{self.full_name}"
end
end
and a Bill class which embeds many :votes. This is designed to allow a user to associate their vote with different groups ("Ruby Coders", "Women", etc.) and is working well, except the database currently allows a user to vote multiple times on one bill. How can I get the following to work?
u = User.last
b = Bill.last
u.vote_on(b,:nay)
u.vote_on(b,:nay) -> should return a validation error
Most probably validators on Vote are not getting fired. You can confirm that by adding a validates function and outputting something or raising an exception in it.
class Vote
validate :dummy_validator_to_confirmation
def dummy_validator_to_confirmation
raise "What the hell, it is being called, then why my validations are not working?"
end
end
If after creating above validations User#vote_on doesn't raises exception, it confirms that callbacks are not fired for Vote via vote_on method. You need to change your code to fire callbacks on Vote. Probably changing it to resemble following would help:
def vote_on(bill, value)
if my_groups = self.polco_groups # test to make sure the user is a member of a group
my_groups.each do |g|
# TODO see if already voted
vote = bill.votes.new(:value => value, :user_id => self.id, :polco_group_id => g.id, :type => g.type)
vote.save
end
else
raise "no polco_groups for this user" # #{self.full_name}"
end
end
There is an open issue on mongoid github issue tracker to allow cascade callbacks to embedded documents. Right now callbacks are only fired on document on which persistence actions are taking place on.

Rails validation context

I need help with my ActiveRecord model. I have context based validations (mis)using the build-in context options for validations:
validates :foo, :on => :bar, :presence => true
model = Model.new
model.foo = nil
model.valid? # => true
model.save # works as expected
model.valid?(:bar) # => false
model.save(:context => :bar) # fails and returns false
But using my model in a accepts_nested_attributes_for :model and calling parent.save fails (the validation gets called and returns false), any suggestions or solutions?
Still no answer? To explain more about my problem: I have a model called Form which has many Fields. Users should see validation errors on submit, but the form should be saved anyway (with and without errors). There are different types of Fields, each with global validations (to ensure database consistency) and its own specific user-defined validations (to validate user-entered data). So my Fields look someway like that:
# Global validations, to ensure database consistency
# If this validations fail, the record should not be saved!
validates_associated :form, :on => :global
...
# Specific user-defined validations
# If this validations fail, the record should be saved but marked as invalid. (Which is done by a before_save filter btw.)
def validate
validations.each do |validation| # Array of `ActiveModel::Validations`, defined by the user and stored in a hash in the database
validation.new(:on => :specific).validate(self)
end
end
In my controller:
# def create
# ...
form.attributes = params[:form]
form.save!(:global)
form.save(:specific)
Is something similar possible in Rails using the built-in functionality? Btw this not my actual code, which is quite complicated. But I hope, you guys will get the idea.
Try conditional validations
class Customer
attr_accessor :managing
validates_presence_of :first_name
validates_presence_of :last_name
with_options :unless => :managing do |o|
o.validates_inclusion_of :city, :in=> ["San Diego","Rochester"]
o.validates_length_of :biography, :minimum => 100
end
end
#customer.managing = true
#customer.attributes = params[:customer]
#customer.save
"Ability to specify multiple contexts when defining a validation" was introduced in Rails 4.1 - check validate method, :on options description
Only for Rails 5+:
You are looking for
with_options on: :custom_event do
validates :foo, presence: true
validates :baz, inclusion: { in: ['b', 'c'] }
end
To validate or save use
model = YourModel.new
# Either
model.valid?(:custom_event)
# Or
model.save(context: :custom_event)
Change has_nested_attributes_for :model to accepts_nested_attributes_for :models.
Hope this helps.
Good luck.

Save collection of updated records all at once

As I understand it, the build method can be used to build up a collection of associated records before saving. Then, when calling save, all the child records will be validated and saved, and if there are validation errors the parent record will have an error reflecting this. First question is, is this correct?
But my main question is, assuming the above is valid, is it possible to do the same thing with updates, not creates? In other words, is there a way to update several records in a collection associated with a parent record, then save the parent record and have all the updates take place at once (with an error in the parent if there are validation errors in the children)?
Edit: So to summarize, I'm wondering the right way to handle a case where a parent record and several associated child records need to be updated and saved all at once, with any errors aborting the whole save process.
Firstly +1 to #andrea for Transactions - cool stuff I didn't know
But easiest way here to go is to use accepts_nested_attributes_for method for model.
Lets make an example. We have got two models: Post title:string and Comment body:string post:references
lets look into models:
class Post < ActiveRecord::Base
has_many :comments
validates :title, :presence => true
accepts_nested_attributes_for :comments # this is our hero
end
class Comment < ActiveRecord::Base
belongs_to :post
validates :body, :presence => true
end
You see: we have got some validations here. So let's go to rails console to do some tests:
post = Post.new
post.save
#=> false
post.errors
#=> #<OrderedHash {:title=>["can't be blank"]}>
post.title = "My post title"
# now the interesting: adding association
# one comment is ok and second with __empty__ body
post.comments_attributes = [{:body => "My cooment"}, {:body => nil}]
post.save
#=> false
post.errors
#=> #<OrderedHash {:"comments.body"=>["can't be blank"]}>
# Cool! everything works fine
# let's now cleean our comments and add some new valid
post.comments.destroy_all
post.comments_attributes = [{:body => "first comment"}, {:body => "second comment"}]
post.save
#=> true
Great! All works fine.
Now lets do the same things with update:
post = Post.last
post.comments.count # We have got already two comments with ID:1 and ID:2
#=> 2
# Lets change first comment's body
post.comments_attributes = [{:id => 1, :body => "Changed body"}] # second comment isn't changed
post.save
#=> true
# Now let's check validation
post.comments_attributes => [{:id => 1, :body => nil}]
post.save
#=> false
post.errors
#=> #<OrderedHash {:"comments.body"=>["can't be blank"]}>
This works!
SO how can you use it. In your models the same way, and in views like common forms but with fields_for tag for association. Also you can use very deep nesting for your association with validations nd it will work perfect.
Try using validates_associated :some_child_records in your Patient class.
If you only want this to occur on updates, just use the :on option like validates_associated :some_child_records, :on => :update
More info here:
http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_associated

Resources