how to get next ID in a table in Rails - ruby-on-rails

I have a table that has following columns
id store_id store_name
id and store_id are always going to be same. Since I am new to rails...I had created it by mistake. But I can't afford to go back and change stuff now since I am using store_id at a lot of places.
Issue 1: I am making admin screen for this table. When I try to insert a record, eventhough, id gets inserted (rails automatically gets the next one) NULL is being inserted in store_id.
Is there any ways I can have same number in store_id as in id while inserting record using the create method. Is there something that can be put in the model so that it is always execute before creating a record. and there I can have access to the next Id that should be inserted?
Issue 2: Since I am making admin screens for this table now..before I was just inserting data into this table by hand. I have inserted 5 records by hand so id and store_id 1..5 already exist. Now when I try to insert a record from admin screen it says duplicate key violates constraint. could it be trying to insert id of 1 since this is the first time I am inserting record to this table using rails?

Issue 1: Until you can remove the store_id column, override the getter in the model for compatibility.
def store_id
id
end
As for Issue 2: make sure your create code is not manually setting the id. The next record should be inserted with id 6, as long as your object has no id defined. Try it from script/console.

I think you should just change your code to only use the ID.
A competent editor will be able to tell you where you have used store id in your project, and your tests will help if you introduce a regression error.
Otherwise, you could use a database trigger. Or setup a callback on the model to set store_id from the newly created id value (look at the after_create callback). Again, both of these approaches are hacks to cover up a bug in your system with an easy fix.

You can set custom primary key for ActiveRecord models:
class Store < ActiveRecord::Base
set_primary_key :store_id
end
Then you can create a new migration that removes 'id' column and updates 'store_id' column to be a primary key.

Related

Rails accepts_nested_attributes_for with id that does not autoincrement

I've got a model B whose primary key id does not auto-increment (because its values are guids from another system).
I get an error when I try to create a new instance of B by setting nested attributes:
ActiveRecord::RecordNotFound: Couldn't find B with ID=076dda41-afa6-4324-b6fd-219d99089dfc for A with ID=846205
a.attributes = {
b_attributes: {
id: '076dda41-afa6-4324-b6fd-219d99089dfc'
}
}
How can I make the assignment build a new instance of B when I specify an id?
This project is on Rails 4.0.1
You can refer one of the samples here
Even I am doing some sort of manipulation.
What I mainly suspecting is id attribute.
In nested attributes, when you pass hash without id then it is considered new record and it is created.
When you pass id it tries to find the record to be updated.
When you pass _destroy it is marked for destruction.
Workaround that I could think of is:
Try making id column NOT primary key so that Rails won't try finding existing record. But in that case you will need to take care of update part.
OR
Create a new column guid where you will have this identifier and maintain id column as Rails default one. Just that setup all the associations on guid

Stronger Active Record IDs

How can we get Active Record to create more complex IDs that are not just the natural numbers (1, 2, 3 ...). I'm not sure if I should be using database IDs to identify objects, but I am now and it would be pretty insecure to have them like this in production.
Lets just say you want to use a column called slug for an object called Post.
Create a slug column for that table
Create a before_create method in Post model that will generate a unique slug value before saving the record. Maybe do some checking to ensure it’s unique
Replace anything that’s public facing to use Post.find_by(slug: id) instead of Post.find(id).
Ensure any url generation will use something like this: post_path(id: post.slug) so that it won't put the id the URL.

rails 3.1 active record insert or update

I'm new to rails.
Is there an easy way in active record to pass it a hash of data and if the record exists, update it, and if it doesn't, create it?
data = {}
data["my_id"] = 356345
data["description"] = "test123"
w = Descriptions.new(data)
Ideally if I ran the above it would only ever have 1 record, not multiple records each time I ran it.
Assuming you ware wanting the "my_id" bit to be unique you can run
Descriptions.find_or_create_by_my_id(data["my_id"]).update_attributes(data)
An ActiveRecord object in Rails retains its identity in the ID parameter. If the ID is set, Rails will know to update the record in the database with that ID.
save is, in fact, the primary way to create, update, or in any way save an object to the database. Other methods like update_attributes are just sugar that use save at their core.
Yes,There are many good things you will get ModelName.find_or_create_by_name("Summer")
But If you pass Id in save method that will be update and if id is not passed it will create data.
Always, Primary Key in Rails should be "id" but you used "my_id" that may also be the problem.

EF Database First - Mapping-error 3025

New to EF and trying something out with "Database first".
Error 3025: ... :Must specify mapping for all key properties
(PurchaseUsers.PurchaseUsersId) of table PurchaseUsers.
I have in my db 3 tables:
Purchases Participants PurchaseUsers
PurchaseId ParticipantId PurchaseUsersId
... ... PurchaseId
ParticipantID
The table PurchaseUsers is to know which participant(s) is(are) using a purchase.
At first I didn't had the PK on that table but then I got the following error when trying to save a Purchase.
After googling a bit I found out that I had to add a PK to avoid this error.
Unable to update the EntitySet 'PurchaseUsers' because it has a DefiningQuery
and no <InsertFunction> element exists in the <ModificationFunctionMapping> element
to support the current operation.
But adding the PK created the mapping-error and I just can't figure out how to solve this or create a mapping.
The table PurchaseUsers itself isn't visible in my .edmx model, but it's listed in the Store in the Model Browser.
Thanks.
UPDATE
Changed the name of a column in the database today. "Update model from database" added the new columnname to the table in the model, but did not remove the old one.
Had to start from scratch once again.
Looks like the update-function is not working very well.
This is weird. Updating the model from the database should make the model and the database to by in-sync. Try deleting and recreating the model from scratch.

next available record id

#user = User.new
#user.id returns nil but i need to know it before i save. Is it possible ?
YES you can!
I had the same question and investigated the docs.
The ability to solve this question is very related to your database type in fact.
Oracle and Postgresql do have useful functions to easily solve this.
For MySQL(oracle) or SkySQL(open-source) it seems more complicated (but still possible). I would recommend you avoid using these (MySQL/SkySQL) databases if you need advanced database tools.
First you must try to avoid this situation as much as possible in your application design, as it is dangerous to play with IDs before they get saved.
There may be situation where you don't have any other choice:
For instance when two tables are referencing themselves and for security reason you don't allow DELETE or UPDATE on these tables.
When this is the case, you can use the (PostgreSQL, Oracle) database nextval function to generate the next ID number without actually inserting a new record.
Use it in conjunction with the find_by_sql rails method.
To do this with postgreSQL and Rails for instance, choose one of your rails models and add a class method (not an instance method!).
This is possible with the "self" word at the beginning of the method name.
self tells Ruby that this method is usable only by the class, not by its instance variables (the objects created with 'new').
My Rails model:
class MyToy < ActiveRecord::Base
...
def self.my_next_id_sequence
self.find_by_sql "SELECT nextval('my_toys_id_seq') AS my_next_id"
end
end
When you generate a table with a Rails migration, by default Rails automatically creates a column called id and sets it as the primary key's table. To ensure that you don't get any "duplicate primary key error", Rails automatically creates a sequence inside the database and applies it to the id column. For each new record (row) you insert in your table, the database will calculate by itself what will be the next id for your new record.
Rails names this sequence automatically with the table name append with "_id_seq".
The PostgreSQL nextval function must be applied to this sequence as explained here.
Now about find_by_sql, as explained here, it will create an array containing new objects instances of your class. Each of those objects will contain all the columns the SQL statement generates. Those columns will appear in each new object instance under the form of attributes. Even if those attributes don't exist in your class model !
As you wisely realized, our nextval function will only return a single value.
So find_by_sql will create an array containing a single object instance with a single attribute.
To make it easy to read the value of this very attribute, we will name the resulting SQL column with "my_next_id", so our attribute will have the same name.
So that's it. We can use our new method:
my_resulting_array = MyToy.my_next_id_sequence
my_toy_object = my_resulting_array[0]
my_next_id_value = my_toy_object.my_next_id
And use it to solve our dead lock situation :
my_dog = DogModel.create(:name => 'Dogy', :toy_id => my_next_id_value)
a_dog_toy = MyToy.new(:my_dog_id => my_dog.id)
a_dog_toy.id = my_next_id_value
a_dog_toy.save
Be aware that if you don't use your my_next_id_value this id number will be lost forever. (I mean, it won't be used by any record in the future).
The database doesn't wait on you to use it. If somewhere at any time, your application needs to insert a new record in your my_table_example (maybe at the same time as we are playing with my_next_id_sequence), the database will always assign an id number to this new record immediately following the one you generated with my_next_id_sequence, considering that your my_next_id_value is reserved.
This may lead to situations where the records in your my_table_example don't appear to be sorted by the time they were created.
No, you can't get the ID before saving. The ID number comes from the database but the database won't assign the ID until you call save. All this is assuming that you're using ActiveRecord of course.
I had a similar situation. I called the sequence using find_by_sql on my model which returns the model array. I got the id from the first object of the arry. something like below.
Class User < ActiveRecord::Base
set_primary_key 'user_id'
alias user_id= id=
def self.get_sequence_id
self.find_by_sql "select TEST_USER_ID_SEQ.nextval as contact_id from dual"
end
end
and on the class on which you reference the user model,
#users = User.get_sequence_id
user = users[0]
Normally the ID is filled from a database sequence automatically.
In rails you can use the after_create event, which gives you access to the object just after it has been saved (and thus it has the ID). This would cover most cases.
When using Oracle i had the case where I wanted to create the ID ourselves (and not use a sequence), and in this post i provide the details how i did that. In short the code:
# a small patch as proposed by the author of OracleEnhancedAdapter: http://blog.rayapps.com/2008/05/13/activerecord-oracle-enhanced-adapter/#comment-240
# if a ActiveRecord model has a sequence with name "autogenerated", the id will not be filled in from any sequence
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
alias_method :orig_next_sequence_value, :next_sequence_value
def next_sequence_value(sequence_name)
if sequence_name == 'autogenerated'
# we assume id must have gotten a good value before insert!
id
else
orig_next_sequence_value(sequence_name)
end
end
end
while this solution is specific to Oracle-enhanced, i am assuming the other databases will have a similar method that you could redefine.
So, while it is definitely not advised and you want to be absolutely sure why you would not want to use an id generated by a sequence, if it is needed it is most definitely possible.
It is why I love ruby and Ruby on Rails! :)
In Oracle you can get your current sequence value with this query:
SELECT last_number FROM user_sequences where sequence_name='your_sequence_name';
So in your model class, you can put something like this:
class MyModel < ActiveRecord::Base
self.sequence_name = 'your_sequence_name'
def self.my_next_id_sequence
get_data = self.find_by_sql "SELECT last_number FROM user_sequences where sequence_name='your_sequence_name'"
get_data[0].last_number
end
end
And finally, in controller you can get this value with this:
my_sequence_number = MyModel.my_next_id_sequence
So, there is no need to get your next value by using NEXTVAL and you won't lose you ID.
What you could do is User.max(id). which will return the highest ID in the database, you could then add 1. This is not reliable, although might meet your needs.
Since Rails 5 you can simply call next_sequence_value
Note: For Oracle when self.sequence_name is set, requesting next sequence value creates side effect by incrementing sequence value

Resources