views:

751

answers:

3

Can I create a Rails model where the ID auto-increments from 0, not 1? How? Are there any gotchas? I checked here, and it seems I can't:

http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#M001911

If, as it seems, I cannot, is there reason why I can't or shouldn't just manually assign an ID of 0 to the first instance of this class? If I do, will Rails automatically assign an ID of 1 to the next instance created?

+5  A: 

Why would you need to do that? In Rails model, the object id represents the unique id from database which never can be 0 (i speak for mysql, maybe there are exceptions with sequences and serials in postgres).

Eimantas
Thanks Eimantas. I was afraid of that.You asked why I would need to do that? Because the model is "part", belongs_to "project". The first part as the part that encompasses the project as a whole. The software creates it when the user creates a project, before the user creates any smaller parts of their own. It would be nice from a UX perspective if this part appeared as "/part/0" to the user in the browser's location bar, and if the user only ever saw, say, "/part/2" if they had defined 2 parts of their own.
steven_noble
You could use separate counter for each part instead of using primary key for this.Other than that i'd keep part 0 (for whole project) in the project object itself.
Eimantas
I speak for mysql, the default value for auto_increment column is 0, so the first record becomes 0 + 1 = 1, to get the "first record" value as 0 you will have to default the auto_increment value to -1 . So that -1 + 1 = 0 , I Doubt if that is going to be possible.
Anand
That would work only for the first project. When someone would create second project the 0-th part would be project1.parts.count + 1. And that not the thing you'd want.
Eimantas
Anand +1 for the default auto_increment tip. Didn't know that .)
Eimantas
You could redefine to_param in your model to return id - 1. Note that you will have to either redefine every find method you want to use of provide a convertion back from param to id in order to use finds.
Vincent Robert
A: 

For postgres, you might wanna take a look at creating the table manually, e.g.

class MigrationName < ActiveRecord::Migration
  def self.up
    execute <<EOF

create sequence foos_id_seq start 0 increment 1 no cycle;
create table foos (
  id       integer not null default nextval('foos_id_seq'),
  ...
  primary key(id)
);
EOF
  end

  def self.down
    execute <<EOF
drop table foos;
drop sequence foos_id_seq;
EOF
  end
end

Note, the position of the EOF is important.

Omar Qureshi
+1  A: 

ActiveRecord works on the principle that each table has a single primary key column that has no significance within the application domain. That's (partly) why you don't have to define the id column in your create_table migrations. The column name (and type) can be changed, largely to accommodate legacy schemas, and multiple-column primary keys are hard to implement at all.

At the point where you start to ascribe a domain significance to the id, you're starting to break the convention and I'd strongly advise that you create another field/property/member-variable/column/what-have-you to hold that information. Leave the id to being a key.

In the use-case you describe, there's no reason that your controller shouldn't identify the special case where params[:id] == '0' and deal with it appropriately, switching to the "project part", which you identify in some way other than by id. Since you probably want each project to start from 0, I'd guess you'd add something like seq and use that instead of id in your routes, that way your params keys make more sense. Keep the current highest seq value in the project, so it knows what number to assign when a new part is created. Deletions and insertions (assuming sequence matters) are entirely up to you...

Mike Woodhouse
Thanks. I'll give this a go.
steven_noble