Autoincrement postgre DB id field in RAILS - postgresql

I am trying to make an autoincrement id field into a postgre DB in rails. I am trying to get the maximum id and then just add +1 and pass it down to create method. I am not quite sure what I am doing wrong, but if I have 4 projects and add the 5th one, that is getting id 5, but if I delete that one and try to add another one, that's getting id 6, not 5 again. Here is my code:
# GET /admin/projects/new
def new
#project = Project.new
#project.id = Project.maximum(:id).next.to_i
respond_to do |format|
format.html # new.html.erb
end
end
# GET /admin/projects/1/edit
def edit
#project = Project.find(params[:id])
end
# POST /admin/projects
def create
#project = Project.create(params[:project])
respond_to do |format|
if #project.save
format.html { redirect_to projects_path, notice: 'Project was successfully created.' }
else
format.html { render action: "new" }
end
end
end
I tried to add #project.save to new, but as much as I understand, the .save should only be done in the create method. Any ideas will be kindly appreciated :D

The auto increment fields goes up acordingly to a sequence, if the value 5 has already been taken(even if the entre has been deleted after, but the 5 has been associated to a record) the sequence dont stop, it will give you the 6th and so on...
Thankfully you can reset the sequence by 2 methods.
1- Truncate the table, truncate also resets the sequences to 1.
2- Use the Alter sequence method, you have here the official documentation for this method and here another question regarding this same matter with an example to solve it.
wich is:
ALTER SEQUENCE ${table}_${column}_seq RESTART WITH 1453
Hope it helps.
UPDATE 1:
With the update info on the coments, the awnser changes.
it is needed to create a custom method that handles that.
Project model
before_create :check_max_value
def check_max_value
self.project_id = Project.last.project_id + 1
end
you need to create a migration to add the "project_id" to the table, that field is the name of that "id you want".
You really shoudn't use the "ID" field created by default by rails.

A few problems here,
First, is the :id field actually in the view in a hidden field? If not, it's not returned in the params.
Second, if you did explicity set :id to the value of a deleted record, PostgreSQL will complain about a duplicate key value regardless. The id is still being used, you just can't access the record.
Third, you should not be assigning a value to :id, ever. :id has only one purpose and that's to provide a unique key for the record in the database, it should not have any other ancillary meaning. It increments automatically, that's all part of how the PostgreSQL adapter works.
If you want some sort of sequential numbering that can be reset, use a different, new field.

Related

Rails 4+: only select specific columns from an included association

I have a user model that belongs to to an account (pretty standard). On every request, I need to retrieve the current_user but I also want to return the account to reduce DB queries:
# ApplicationController
def current_user
#current_user ||= User.includes(:account).find_by(auth_token: cookies[:auth_token])
end
def current_account
#current_account ||= current_user.account
end
This works just fine. However, the account record has a lot of extra text data that I don't always want to return. A more permanent solution would be to move those extra fields to a new table called account_details and have a one-to-one between an account and account_details.
However, I need a shorter-term solution that will provide some optimizations until I can refactor that (perfection is an iterative process!).
I want to return ALL columns from the current_user, but I only want to select a few columns from the current_account.
I tried this and I didn't work:
#current_user ||= User.includes(:account).references(:account).select("users.*, account.id, account.uuid").find_by(auth_token: cookies[:auth_token])
When I look at #current_user.account it still displays all the columns with their values and I only wanted the id, uuid columns.
I couldn't find any clear syntax on using select(...) with includes(:association); is this something that's possible?
You need to add an extra association in your User model.
belongs_to :account_columns, -> { select(:id, :uuid) }, class_name: 'Accoun'
This association should be used in your query.
User.includes(:account_columns)
This is because Rails don't have the facility to pass the options for include query.
Also consider using join instead includes in your query, as it also caters your need.
User.joins(:accounts).select("accounts_ids, accounts.uuid")

find_or_create_by - id = nil when record exists

I'm trying to use ActiveRecord's find_or_create_by method and I need the ID of returned record. When this method creates a new record this happens. However, if the record already exists, then the ID (along with the created_at and updated_at columns) is nil. All other data is returned just fine. Why is this? I can see it in my database and it has an ID. When I use find_by to get the record, the ID is returned. Is this a bug or am I missing something?
I know this is an old question, but I had the same issue and it was a pretty simple user error.
The id of your object will be nil if no persisted object was found for the selected attributes, but it can't be fully created (initialized and saved to the db) because it doesn't pass validations.
TLDR; If you use Model.find_or_create_by(attributes) and wind up with an object that has id: nil, check the errors on it, adjust your attributes, and happy coding.
As RachelS says but worse. This seems to happen if validations fail regardless if the record is found first. This is likely to come up if you have uniqueness validations on a column.
class Model < ApplicationRecord
validates :name, uniqueness: true
end
record = Model.create_or_find_by(name: 'test')
record.id # 1
record2 = Model.create_or_find_by(name: 'test')
record2.id # nil
record2.errors # #<ActiveModel::Errors [#<ActiveModel::Error attribute=name, type=taken, options={:value=>"test"}>]>
Unfortunately, this seems intended but basically makes create_or_find_by unusable as you'll normally want to have uniqueness validation on a unique indexed column which is a prerequisite to create_or_find_by in the first place...

Code to assign an ID when button is clicked

I have designed a simple database to keep track of company contacts. Now, I am building a form to allow employees to add contacts to the database.
On the form itself, I have all the columns except the primary key (contactID) tied to a text box. I would like the contactID value to be (the total number of entered contacts + 1) when the Add button is clicked. Basically, the first contact entered will have a contactID of 1 (0 + 1 = 1). Maybe the COUNT command factors in?
So, I am looking for assistance with what code I should place in the .Click event. Perhaps it would help to know how similar FoxPro is to SQL.
Thanks
The method you recommend for assigning ContactIDs is not a good idea. If two people are using the application at the same time, they could each create a record with the same ContactID.
My recommendation is that you use VFP's AutoIncrementing Integer capability. That is, set the relevant column to be Integer (AutoInc) in the Table Designer. Then, each new row gets the next available value, but you don't have to do any work to make it happen.
There are various ways to do this. Probably the simplest is to attempt to lock the table with flock() when saving, and if successful do:
calc max id_field to lnMax
Then when inserting your new record use lnMax+1 as the id_field value. Don't forget to
unlock all
... after saving. You'll want to ensure that 'id_field' has an index tag on it, and that you handle the case where someone else might have the table locked.
You can also do it more 'automagically' with a stored procedure.

Generating a customer ID in a form

I have a customer table 'tblCustomer' that contains a primary key column called CustID. CustID refers to the Customer as their own ID number.
Now, I am in the process of creating a form that will allow people to add new customers. To do that, I need to generate a new CustID. My CustID starts at '10000' and go up by 1 each time. It is important to note that this field is a text field, not a number field.
There must be an ID, otherwise the record cannot be created as the primary key field is a mandatory field and is also tied to other relationships in the database.
As such, I am creating a button next to the ID field on the form that will generate an ID in the textbox. It needs to start at whatever the custID's highest key value is and increment it by 1. The problem is that it is a text field and cannot change.
I am unsure how to do this, personally. I have tackled it quite a few ways but all lead me back to the same spot - I don't know how to do this with custID being a text value rather than a numerical value. I have been thinking of doing a conversion of some sort as the calculation occurs but I have no idea how to do this at all.
I'm a bit of a newbie with Access, so any help would be appreciated!
Assuming that all CustID's are numbers, you can do the following to get a new ID:
Dim strMax As String
Dim intMax As Integer
strMax = DMax("CustID", "<your table name>")
intMax = CInt(strMax) + 1
strMax will hold the current maximum id, and intMax will hold the new ID. If strMax contains a non numeric value however this code will fail - you would need an error handler to work around it.
Just need to put it in the buttons click event.
Hope this helps!
Simon

Get next available auto_increment ID in PostgreSQL - A better approach?

I'm new to postgreSQL, so would really appreciate any pointers from the community.
I am updating some functionality in the CMS of a pretty old site I've just inherited. Basically, I need the ID of an article before it is inserted into the database. Is there anyway anyway to check the next value that will be used by a sequence before a database session (insert) has begun?
At first I thought I could use SELECT max(id) from tbl_name, however as the id is auto incremented from a sequence and articles are often deleted, it obviously won't return a correct id for the next value in the sequence.
As the article isn't in the database yet, and a database session hasn't started, it seems I can't use the currval() functionality of postgreSQL. Furthermore if I use nextval() it auto increments the sequence before the data is inserted (the insert also auto-incrementing the sequence ending up with the sequence being doubly incremented).
The way I am getting around it at the moment is as follows:
function get_next_id()
{
$SQL = "select nextval('table_id_seq')";
$response = $this->db_query($SQL);
$arr = pg_fetch_array($query_response, NULL, PGSQL_ASSOC);
$id = (empty($arr['nextval'])) ? 'NULL' : intval($arr['nextval']);
$new_id = $id-1;
$SQL = "select setval('table_id_seq', {$new_id})";
$this->db_query($SQL);
return $id;
}
I use SELECT nextval('table_id_seq') to get the next ID in the sequence. As this increments the sequence I then immediately use SELECT setval('table_id_seq',$id) to set the sequence back to it's original value. That way when the user submits the data and the code finally hits the INSERT statement, it auto increments and the ID before the insert and after the insert are identical.
While this works for me, I'm not too hot on postgreSQL and wonder if it could cause any problems down the line, or if their isn't a better method? Is there no way to check the next value of a sequence without auto-incrementing it?
If it helps I'm using postgresql 7.2
Folks - there are reasons to get the ID before inserting a record. For example, I have an application that stores the ID as part of the text that is inserted into another field. There are only two ways to do this.
1) Regardless of the method, get the ID before inserting to include in my INSERT statement
2) INSERT, get the the ID (again, regardless of how (SELECT ... or from INSERT ... RETURNING id;)), update the record's text field that includes the ID
Many of the comments and answers assumed the OP was doing something wrong... which is... wrong. The OP clearly stated "Basically, I need the ID of an article before it is inserted into the database". It should not matter why the OP wants/needs to do this - just answer the question.
My solution opted to get the ID up front; so I do nextval() and setval() as necessary to achieve my needed result.
Disclaimer: Not sure about 7.2 as I have never used that.
Apparently your ID column is defined to get its default value from the sequence (probably because it's defined as serial although I don't know if that was available in 7.x).
If you remove the default but keep the sequence, then you can retrieve the next ID using nextval() before inserting the new row.
Removing the default value for the column will require you to always provide an ID during insert (by retrieving it from the sequence). If you are doing that anyway, then I don't see a problem. If you want to cater for both scenarios, create a before insert trigger (does 7.x have them?) that checks if the ID column has a value, if not retrieve a new value from the sequence otherwise leave it alone.
The real question though is: why do you need the ID before insert. You could simply send the row to the server and then get the generated id by calling curval()
But again: you should really (I mean really) talk to the customer to upgrade to a recent version of Postgres