Rails update_attributes RecordNotUnique - ruby-on-rails

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')})

Related

Why does rails think my object isn't persisted?

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.

Rails Console: Create a New Record Using an Existing Record

Recently I had to create a couple of records in a non-rails app database table based on a previous record. It got me thinking of how would I do this in a rails app. I tried a couple of things in the Console, but nothing works.
I want to do something like this:
001> user = User.new(User.first)
I know this doesn't work but hopefully it will show you what I an thinking. User is a large table/model, and I only need to change a few fields. So, if I can set up a new record with the same values in User.first, I can then edit the fields I need to before .save-ing the record.
Thanks for any help.
I think what you want is:
user = User.first.dup
user.assign_attributes(email: "myemail#test.test")
user.save
The first line uses dup to create a copy of the object. The copy is not yet saved to the database. Replace dup with clone if you're using an old version of Rails (<3.1).
In the second line, assign_attributes alters the attributes of the object, still without saving it to the database. If you were working with an object already saved in the database, you could use update instead of assign_attributes to change the attributes of the object and save the changes in one go. That won't work here, because we haven't saved our duplicate user yet. More details on that here.
The third line finally saves the new object to the database. It saves time to just do this once, at the end.

Delete record with nil id in rails console heroku ruby postgres

I used destroy to delete a record remotely in heroku rails console and now it does not show up if I write
MyModel.find_by(email: 'some#email.com')
but it does show up if I write
MyModel.find_by_or_create_by(email: 'some#email.com')
...except the id is nil. I can't figure out how to get rid of this record. I am using postgres and rails 4
When I try to create a new record with the same email via the web ui, it triggers the uniqueness validation for this ghost record...yet I can't remove the ghost record.
When find_or_create_by returns a record with a nil id, that suggests the find part is failing, and then the create part fails too with validation errors. What do you get from MyModel.find_or_create_by(email: 'some#email.com').errors.full_messages? I'm guessing you see the same uniqueness validation error as you're seeing in the web console.
Is your app using a soft-delete approach, e.g. with a gem like acts_as_paranoid or permanent_records? Those gems change the behavior of destroy so that it does not issue a SQL DELETE command but instead sets a deleted_at column. They also hide soft-deleted records, so that may be why find_by isn't giving you anything. If this is what you're doing, you should make sure your uniqueness validation knows to ignore soft-deleted records. How to do that depends on your soft-delete implementation, but you might find some tips here.
You might want to try straight SQL to see what's really in your database, e.g. using the Heroku psql prompt or this Ruby code: MyModel.unscoped.where(email: "some#email.com")
find_or_create_by will look for the record by the given parameters and create it if it can't be found. If there isn't an id field that means it isn't being saved and your problem is already solved.

Active Record object loaded from database is invalid

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.

mongoid atomic updates in rails

i am having an issue with updating existing documents composite key fields in rails. the associated mongo query for my update_attributes statement seems to be correct, however the object cannot be found afterwards.
for example with an existing object with first_name "Jane" and last_name "Doe"... with my :key being :first_name, :last_name
i hit my update method with:
{"artist"=>{"last_name"=>"Doe", "first_name"=>"John"}, "commit"=>"Update Artist", "id"=>"jane-doe"}
def update
#artist = Artist.find(params[:id])
if #artist.update_attributes(params[:artist])
redirect_to #artist
end
end
which generates the mongo query: MONGODB app_database['artists'].update({"_id"=>"jane-doe"}, {"$set"=>{"_id"=>"john-doe", "first_name"=>"John"}})
which seems correct to me... but when i am redirected to that new artist, it complains about Document not found for class Artist with id(s) john-doe.
and in fact looking at my db from the mongo console, i still see Jane Doe in there. It is something to do with them being composite key fields, since i can update non-key fields just fine.
what am i missing here?
I tried this in an app of my own, and it looks like MongoDB simply doesn't currently allow you to modify the _id field as part of a $set operation (which is what Mongoid uses to perform updates). This is a strange restriction - I've been using Mongo for a year and a half now and I've never run into it.
So, a few options:
Stop using anything for your _id that might need to be changed later. I'd do this - it's a best practice anyway.
Whenever you need to make one of these _id changes, instead create a new record with the new _id attribute and delete the old one. This might get messy though, especially if you have other models that refer your Artist models by id.
File an issue with 10gen asking for this restriction to be lifted. They're very good about responding to users' concerns, but even if they agree, it'll probably take a while to be done.
File an issue with Mongoid to request support for these types of changes (Mongoid could conceivably handle the create + delete mechanism for you), but honestly, it's the kind of edge case that's probably not worth the extra time and code for them to support. It'd be nice if Mongoid raised an error when you tried to do an update like this, at the very least.

Resources