I have a small database and have been adding entries through a Rails page. I "destroyed" one of the entries and now my sequence of IDs are skipping by one. For example, I now have 42 then 44, instead of the obvious: 42, 43, 44.
I was wondering if there was a way to edit the ID number of a new object through the console. I have tried:
record.id = 43
record.save
and
record = record.new
record.attributes = { :id => 43 }
but both didn't work. I'm fairly certain there has to be a console method for this, but I can't seem to find much specific on Google and I probably read the Rails API incorrectly... Would I possibly have to do this through direct SQL in sqlite?
Thanks
The best way to do it is to execute the SQL directly, and solve this temporal glitch in the sequence.
Try accessing the console (ruby script/console) and type:
>> sql = "update records set id=43 where id=44"
>> ActiveRecord::Base.connection.execute(sql)
Where 44 is the newly created object's id, and 43 is the one you were missing in your table
Good luck!
Actually, you can set the id manually for new objects:
record = Record.new
record.id = 1234
record.save
However, what you're trying to do is update the id of an existing object. When you set record.id = 43 and then call save what happens is that ActiveRecord will try to generate SQL like this:
update records set id = 43 where id = 43
Notice that the id it's looking for to update is the same as the one you're trying to change. That's why it doesn't work.
So yes, you would have to use SQL to change this. Whether or not that's a good idea is another issue, but sometimes it needs to be done.
Would I possibly have to do this through direct SQL in sqlite?
Yes.
The whole point of ActiveRecord is that is abstracts DB functions and just returns collections of data. You shouldn't be worrying about the ID of a record, that is something specific to the DB. Off the top of my head I can't think of any reasons to reference the model's ID.
If your app depends on having a sequenced number then you should add another field to the model which has this. For instance, if I have a store with products (a Product model) and I give the ID number the DB provides to other vendors. Two weeks later, my boss asks me to have a unique, but similar ID for two variations of products: "45a" and "45b". Nuts. The ID field should only be used by to the database and ActiveRecord, not you or your users, to identify the record.
There is a small chance that there might be an obscure method which force sets the ID if the DB allows it, but it is obscure for a reason. Don't try and find it :)
All that being said, type ruby script/dbconsole to quickly pull up the sqlite interface without having to type your password.
Also, if you delete the sqlite database that will reset the counter and start at 0. With great power comes great responsibility.
EDIT
If I remember correctly Dave Thomas wrote about this somewhere. Perhaps here?
Related
In our database, we have a table for comments and blogs.
There's a field comments.comment_blog_index that increments for each comment in the blog.
...so if we have 3 comments for a particular blog, the value for comment_blog_index for each comment is: 1, 2, 3 (respectively)
The code that sets comment_blog_index looks like this:
#comment = Comment.new
#comment.comment_blog_index = #blog.comments.count + 1
The problem happens when two users trigger this code simultaneously. It will calculate the same value for both users, and the comment_blog_index is duplicated.
I've seen code for Item.increment_counter( :total_bids, item.id ), but that requires you to already have a record in the database in a table that stores a summation. In our case, the record is being created inside the `commments`` table.
How do we prevent this?
As you've already seen your current method is not safe. You could add validations and callbacks and all manner of things to try and make it safe but I would suggest that kind of work should happen at the database level.
Either implement a propert auto-incrementing field or offload that work to a gem.
It will help
Generate an auto increment field in rails
It shows how auto increment can be implemented in rails
rails console
u = User.find(9)
u.id = 7 # There is no other record with id 7
u.save
=> true
User.all
The id has not changed.
How to change the primary ID? Why does it prevent this action?
Working in Rails 3.0.7 and PostgreSQL.
EDIT:
Since there are good reasons not to do this, I'll explain why and hopefully it is a good reason.
We are doing Usability Testing on Staging, so I want it to look like the Production to make it easy for everyone. I don't want to create confusion or errors in the Usability Testing process by having some things in one order on Staging and in a different order on Production. I only changed one PK id on Staging DB.
Don't do this on your production DB!
I'm not sure as to why it prevents that action, but it does for sure prevent it.
You can bypass this using the update_all method on for the user.
User.where(id: 7).update_all(id: 9)
Though if possible, you really should avoid doing this.
For me worked:
User.find(9).update_column(:id, 7)
Could you elaborate what your use case is? Why do you want to change the ID? What are you really trying to accomplish with it?
Generally it's a bad idea to do this, and Rails won't let you do this easily because it will break your data integrity!
Here's Why:
When you're using a relational database (like PostgreSQL) underneath, you will have relationships between your models, which means that you will use the model's IDs as a reference in other related models... which means that if you change an entry's ID , all those references to that entry will go stale and corrupt your database..
I would strongly suggest to analyze your requirements again, and try to find another way to accomplish what you need to do (without changing IDs)
#Jason pointed out a very valid point. I totally agree with him. But, you might have your reasons and I suggest you re-consider what you're trying to do.
To answer your question:
ID columns are protected by default for mass assignment and cannot be set manually. But, you can override this behavior by defining a method:
def self.attributes_protected_by_default
[] # ["id", ..other]
end
This will allow you to assign id's manually.
Another method (although it is not pure Rails) is to create a new column, and populate it with your new IDs.
Then, using DB management software (not Rails), remove the Primary Key attribute from the id column, delete it, rename your recently added column to "id", and give it the Primary Key attributes. (If you cannot do that directly in your DB software, then set the properties Unique, Auto-Increment, etc.)
You can also move the column to the front (MySQL syntax):
ALTER TABLE table_name MODIFY COLUMN id int(11) FIRST;
But there is another thing I'd really like to say. It hasn't been as bad on this question as I've seen elsewhere, but folks: it's all well and good to tell people it's USUALLY not a good idea, but that isn't an answer to the question. Please refrain from saying "Don't do that" unless you already know the person's use-case.
In other forums I've been greatly frustrated by people saying "Why do you want to do that?" or "Don't do that", and then not answering the question. They didn't give me credit for already KNOWING that it isn't standard practice, and they ASSUMED I didn't already know that it was not an ordinary use-case.
People on this page haven't been that bad, and I'm not trying to pick on them. I'm just admonishing: please, check your own behavior before presuming to lecture. Somebody asked a question, and while warnings may be a good idea, it is probably safe to presume they have a REASON for wanting an answer.
End of rant.
The ID is typically generated by the database as an auto-incrementing PK. You can't (and really shouldn't need) to modify it.
in the application i am currently creating in ruby on rails. I am trying to do some tests in rails console where i have to destroy data in the database and the database is connected to a server. I am importing an XML and parsing it and putting it into a database with scaffolding.
Now what i need: Basically what i am attempting to do is to destroy the data and replace it with a new one every week..but the problem i am getting, the userid is gone up to 700+ and there are only 50 records :S cause it doesnt reset...
To delete all records i am currently using "whatever.destroy_all" does the trick
Any help?
Btw i am using SQLITE
The ID column created in the table usually is set as unique and to increment by 1 for each new record, which is why each time you destroy and add new data the ID keeps getting higher.
The fact that the ID # is getting larger and larger is not an issue at all.
If you really want to start back at zero, I would think you could drop the table and recreate it, but that seems like overkill for a trivial issue.
Regarding the connection to the other scaffold, how are you connecting the two and what do they both represent?
Ideally the data population for testing should be done through fixtures (or easy tools such as factorygirl etc..)
The main advantage of having a fix data set is you can run your tests in any environment. But as per your requirement you can do something like this,
When you populate the date through the active records pass the id parameter as well
Ex: User.new(:id => 1, :name => "sameera").create
By this way you can have constant id's But make sure you increment the id accordingly.
I want to use ActiveRecord transactions to save three of my models. I was able to find some good tutorials like
http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
http://ar.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
But my question is, I want to get my first model's id and save it as a foreign key with my second model. As far as I know I couldn't do this because I cannot get the saved id until the transaction commits.
Does anyone have a better solution or workaround for this problem? I'm using Rails 2.3.8.
Generally when you need to save objects with relationships, it means you should use accepts_nested_attributes_for. See doc here.
It's by far the cleanest and the best way to proceed.
What about:
SomeClass.transaction do
first_record = SomeClass.create(... vars ...)
second_record = SomeClass.create(... vars ...)
third_record = SomeClass.create(... vars ...)
second_record.update_attributes(:related_id=>first_record.id)
third_record.update_attributes(:related_id=>second_record.id)
end
That would create the objects, and assign the ids in one overall transaction. If it fails, all records should be rolled back, including the creation of the records in the first place.
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 }