I have an ActiveRecord object that I load from database.
When I call valid? on this object it returns false due to a rails unique constraint not met, at least so the validation says.
I checked the database schema and the unique field also has an index defined, so the uniqueness is also ensured on the database level.
What is going on here and how is this even possible in the first place?
You should check #object.errors.inspect for inspection of what's going on and then fix accordingly.
Also it does matter that when are you checking the validity of an object i.e. before save or after save.
The more elegant way is to use #object.save!
Ruby should tell you what went wrong during the attempt to save the object.
If you do not have unique indexes defined on your database tables, this is what happens!
To be a bit more elaborate: I thought the database had a unique index on the column, but that turned out to be a 'regular' index.
The problem occurred, because at some point in the application, the model got saved without validating it first. Which led to non unique entries in the database. By calling valid? triggers the rails internal routine that checks for uniqueness (however that is implemented) , which returned false, correctly.
Lesson learned: Always make sure to add a unique index at the database level.
Related
I have a situation where a myobject.save! is resulting in this error:
PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "things_pkey"
DETAIL: Key (id)=(12345) already exists.
: INSERT INTO "things" ("id", ...) VALUES (12345, ...) RETURNING "id"
So, rails has a persisted record but tries to do an insert instead of an update, and then includes the id in the insert (because, I'm guessing, in the insert case it's not accustomed to excluding any columns).
Further up in the code, there is a save! on the same object which may or may not have fired in the case I'm looking at. The only thing notable about this save is it happens inside a rescue block. I did some simple tests in console to see if for some reason an object isn't considered persisted if it's created inside a rescue block, and didn't find any such behavior.
What could be causing rails to think my object isn't persisted?
Figured it out!
I was building the object with user.things.build. User#things was not an association. It's a method which returns an ActiveRecord::Relation. This method had recently changed, to be Things.where(id: ...). So, rails was obediently using as much of the query as possible when building the new object.
I am not sure if I understand totally active record validation role.
Of course, if a user inputs data (like an email or a country), I can and should validate its existence, its uniqueness or its inclusion in a list of countries
But for example, if I have methods in the backend that change an attribute page_clicked or click_date or even the column update_at, that I "control" i.e 'is not generated by a user's input', should I use active record validations ?
I'm asking this because on a very 'hot database' (need speed for millions of frequent updates), I wonder if checking on each update that updated_at is a datetime, and that if a clicked column is true/false and nothing esle is really necessary as the user is not the one inputting/controlling these data but I am through Rails custom methods I wrote
Thanks
I don't think there is a general satisfying answer to your question. It's up to you to enforce validation or not.
Remember that you don't have to use ActiveRecord for validation, you can also use your DBMS to ensure that:
a value will never be NULL (one of the most annoying errors)
a value has the correct TYPE
a FOREIGN KEY always points to an existing row in another table
and depending on your DBMS, a lot more is possible
If you need high INSERT speed and want to go with raw SQL INSERTS, putting some validation in your database can prevent nasty application errors later.
Validations should guard your database and its job should be to stop saving the records that are considered invalid by your application.
There is no hard rule on what is valid record you have to decide it your self by adding the validations. If the record wont pass the validation step it is simply not going to be saved to the database.
From Active Record Callbacks:
3.1 Creating an Object
before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit/after_rollback
3.2 Updating an Object
before_validation
after_validation
before_save
around_save
before_update
around_update
after_update
after_save
after_commit/after_rollback
You can see that validation hooks run at the beginning of the object life cycle.
So in your case instead of asking your self a question:
Should I use active record validations if the record is not generated by a user's input.
You should ask your self:
Is this record invalid without page_clicked or click_date(aka them being nil)
UPDATE
If you consider record to be invalid but worrying about speed problems with running validations I would do the validations to make sure that all the records in the database are valid and try to find the way to optimise the speed somewhere else. Plus not 100% sure but time spend on saving invalid records and filtering them later on will be probably much longer then validating in the first place.
When performance is really a priority and that I am sure that we developers / the server are the only ones who can manipulate specific attributes of a Model, I will
Make sure that I create a separate method / wrapper method for this specific action.
In this specific method, I call .save (validate: false) instead of the usual .save
I still write validations for the said attributes for developers' reference to prevent future development errors, and in case a new developer comes in and accidentally save an invalid record, precisely just because there's no validation to safeguard it.
Or, I will use .update_column instead of .save (validate: false) to perform a direct DB call, skipping Model validations and callbacks (If you also do not want callbacks to be called).
Note that .update_column is different from .update.
I am trying to overwrite a record in rails 4.0.
old_p.update_attributes(new_p.attributes)
old_p is the record pulled from the database, new_p is the record the user has created that will replace the record from the database. new_p actually has its own record in the database, but is only stored there temporarily.
This seems to work some of the times, but most of the time it comes back with
ActiveRecord::RecordNotUnique in Controller#overwrite
PG::UniqueViolation: ERROR: duplicate key value violates unique constraint
This is an upgrade from rails 2, and seemed to have been working as expected when it was a rails 2 app. There seems to be little documentation on update_attributes, but it seems as if it is copying the id of the object as well.
I have also tried assign_attributes with .save later, but to the same effect.
If it is copying the id of the object as well, is there a way to easily leave out the id? As the record has some 20+ attributes I would have to manually enter and they could change often. Or is there something else that I am missing?
You can use
old_p.update_attributes(new_p.attributes.tap { |h| h.delete('id')})
I have a table and have the validation for uniqueness setup in the table. eg.
create table posts (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY UNIQUE,
title varchar(255) unique,
content text
);
Here title is unique. Do also need to inform the model class about this uniqueness? If not when i insert a duplicate title, it gives me error. How do I catch that. Currently rails shows me the backtrace and i could not put my own error messages
def create
#f = Post.new(params[:post])
if #f.save
redirect_to posts_path
else
#flash['message'] = "Duplicated title"
render :action=>'new'
end
end
I am not being redirected to the new and instead show a big backtrace.
Use the validates_uniqueness_of validation. "When the record is created, a check is performed to make sure that no record exists in the database with the given value for the specified attribute (that maps to a column)"
You will have to add all of the validations to your models. There is a gem called schema_validations, which will inspect your db for validations and create them in your models for you. https://github.com/lomba/schema_validations
Yes you do as noted in other answers, the answer is validate_uniqueness_of - http://ar.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M000086. Note, even though you have a validation in your model a race condition does exist where Rails may try and do two inserts unaware of there being a unique record already in the table
When the record is created, a check is performed to make sure that no
record exists in the database with the given value for the specified
attribute (that maps to a column). When the record is updated, the
same check is made but disregarding the record itself.
Because this check is performed outside the database there is still a
chance that duplicate values will be inserted in two parallel
transactions. To guarantee against this you should create a unique
index on the field. See add_index for more information.
So what you have done, by creating a unique index on the database is right, though you may get database driver exceptions in your exception log. There are workarounds for this, such as detecting when inserts happen (through a double click).
The Michael Hartl Rails Tutorial covers uniqueness validation (re. the "email" field) here. It appears the full uniqueness solution is:
Add the :uniqueness validation to the model.
Use a migration to add the unique index to the DB.
Trap the DB error in the controller. Michael's example is the Insoshi people_controller--search for the rescue ActiveRecord::StatementInvalid statement.
Re. #3, it looks like Michael just redirects to the home page on any DB statement exception, so it's not as complex (nor as accurate) as the parsing suggested by #Ransom Briggs, but maybe it's good enough if, as #Omar Qureshi says, the uniqueness constraint covers 99% of the cases.
I have an ActiveRecord object, Corporation, and the only call in my project to create instances of this object looks like:
corp = Corporation.find_or_create_by_eveid_and_user_id(self.corporation_eveid, self.account.user_id)
Yet somehow, after my application has been running happily for a couple of days, there are duplicate records -- record where the eveid and user_id have the same values. How is this possible? Is there something I could be doing wrong in the way I update these records that would cause this problem?
I ended up added a unique, composite index to the table. That should solve the problem, but I don't understand how it's occurring.
This is Rails 3.0.7.
find_or_create does not perform any locking and makes no attempt to prevent race conditions. It's just a convenience method. If race conditions are a problem, you will need to either:
Use a transaction and roll back if you find somebody else has written just before you
(Better if you're actually expecting a race condition), perform pessimistic locking. This is where you SELECT from the table acquiring an exclusive lock first, then perform the write and clear the lock. In MySQL InnoDB tables, this is SELECT ... FOR UPDATE. If you have no reference point to lock on (i.e. no foreign key or anything that already exists in the database), then you'll have to stick with (1).
EDIT | If you can add a UNIQUE constraint at the schema level, I'd advise doing so too, if this is a genuine integrity concern.
Is this in your seeds file? Your best bet would be to write validations in your model to prevent the existence of a Corporation with the same eveid and user_id.
It seems to me that you seeded this information using find_or_create, which worked. But then maybe later in the day or another day someone created another one with the same information using your GUI. Validations would prevent this.
I have not tested this code, but something like this may work for you.
validates :eveid, :uniqueness => { :scope => :user_id }