I have a table called Contracts. Its current default primary key is the :id field that Rails automatically generates, which is an integer. I want to have a field called contractId that is a string type and use it as a primary key instead. What I want to know is:
Is this a best practice? Are there any potential issues with doing this?
How I would go about it
Ruby on Rails (RoR) likes to emphasise the concept of convention over configuration. Therefore, it seeks to minimialise the amount of configuration.
So if you want contractId that is a string type then you can add one extra field in your table and use it wherever you want and let the Rails use id as primarykey.
Change PrimaryKey
Generate a new migration file name it "ChangePrimaryKey" (You can give any name).
class ChangePrimaryKey < ActiveRecord::Migration
def up
remove_column :table, :id # remove existing primary key
rename_column :table, :udid, :id # rename existing UDID column
execute "ALTER TABLE table ADD PRIMARY KEY (id);"
end
def down
# Remove the UDID primary key. Note this would differ based on your database
execute "ALTER TABLE table DROP CONSTRAINT table_pkey;"
rename_column :table, :id, :udid
add_column :table, :id, :primary_key
end
end
If you are creating a new table, your migration might look like this:
class AddTableWithDifferentPrimaryKey < ActiveRecord:Migration
def change
create_table :table, id: false do |t|
t.string :id, null: false
# other columns
t.timestamps
execute "ALTER TABLE table ADD PRIMARY KEY (id);"
end
end
end
Notice the id: false options you pass into the table — this asks Rails not to create a primary key column on your behalf.
Changes to Model
In the model, it is essential that you add the following line in order for
Rails to programmatically find the column you intend to use as your primary key.
class Table < ActiveRecord::Base
self.primary_key = :id
# rest of span
end
I hope you can do rest of the things.
Don't change default id if you want to see Rails real Magics :)
As you may know, Rails supports changing the primary id column out of the box:
class Contract < ActiveRecord::Base
self.primary_key = "contractId"
end
Please note that even if the contractId column has a unique index, an index on a string column will always be a bit slower than an index in an integer column.
Furthermore, this is not the Rails way and might confuse other developers that work with this application. Especially building associations or routes are error-prone when your table has a non-standard primary key. IMHO that is a good reason to avoid using this technic as long as possible.
Related
I am building a rails 3 application with a products model. I've been trying to find a way to use the product id number as the identifier rather that using the models default ID.
I initially tried to use the to_param method, however I could not get it to work correctly.
I then rebuilt my database using the products ID number instead of the primary key as so:
class CreateProducts < ActiveRecord::Migration
def change
create_table :products, :id => false, :primary_key => :prod_id do |t|
t.string "prod_id"
t.string "upc"
t.text "title"
t.text "description"
t.timestamps
end
end
end
And changed my code to find/create using the prod_id. This seems to be working great, however I was hoping to find out a little more about any consequences this may have, and what the disadvantages of not using a primary key maybe.
I was hoping to find out a little more about any consequences this may have, and what the disadvantages of not using a primary key maybe.
You are using a primary key - it's prod_id. You're just not using a default setting auto-incrementing key (presumably though, prod_id is auto-incrementing and uniquely constrained? So you might as well have just left it as id for convention's sake...).
If you tell your model that it's using a different primary key, you shouldn't need to do any other configuration (or specification in associations - the prod_id will be picked up by reflection).
class Product < ActiveRecord::Base
set_primary_key :prod_id
...
end
(different versions of Rails have twiddled with the methods for doing this... check the API for whatever you're using)
There's no magic to choosing to use a non-default primary key - you just need some field that uniquely identifies each row. What it's called, and what its data-type is is practically irrelevant - if you really want to, you can configure your app to use GUID strings as IDs :-)
I have a ActiveRecord model GPA that doesn't have a primary key:
class GPA < ActiveRecord::Base
end
When I try to call GPA.first.to_json I get TypeError: false is not a symbol. I'm guessing that this is due to ActiveRecord trying to lookup the primary key. What is the correct way to implement a model without a primary key?
Normally there is either some column or combination of columns which together do form a primary key. When you say your table doesn't have a primary key do you mean it doesn't have an id field?
Is there another column that is a unique/natural key? If so you can do:
class GPA < ActiveRecord::Base
set_primary_key :strm # eg column strm is a unique/natural key
end
You can also use composite keys with the composite keys gem, as follows:
class GPA < ActiveRecord::Base
set_primary_keys :student_id, :strm
end
You need something like this on your migrations or schema
create_table :gpas, :id => false do |t|
t.int :strm
t.string :gpa
t.string :acad_career
t.int :student_id
end
It is highly advisable to use the rails default conventions when modelling your data. Only override defaults if you are dealing with legacy tables.
Option 1
You have to override the primary key for your model by doing the following:
class Foo < ActiveRecord::Base
self.primary_key = "strm"
end
Now if you call .to_json the json will include the key id instead of strm.
Option 2
Alternative is to use a json constructors such as jbuilder, so it does not try to convert non-existent 'id' to json using default Rails json constructor.
Add a migration that adds id as a column. It's not going to hurt any existing code. Just make sure it's got an autoincrement on it so it's not going to moan about duplicate entry 0.
Have you tried ActiveModel::Serializers::JSON?
I'm trying to use default values for a model in rails. The best documented way of doing this that I have seen is using the :default parameter in a migration. However, it looks like this only works for values. I am trying to set a default expression, specifically currval('customers_id_seq'::regclass). I can add the default expression by hand, but I can't figure out how to tell rails to use a default value. How can I use this default when creating model objects?
EDIT
Here is a more specific example:
class CreateTestmodels < ActiveRecord::Migration
def up
create_table :testmodels do |t|
t.integer :idxfield, :null=>false
t.string :datafield
t.timestamps
end
execute("ALTER TABLE testmodels ALTER COLUMN idxfield SET DEFAULT currval('testmodels_id_seq'::regclass)")
end
def down
drop_table :testmodels
end
end
With this migration, I can run insert into testmodels (datafield) VALUES ('sometestdata');, which will add a row to the table where idxfield defaults to id.
However, if I run Testmodel.create(:datafield=>"testdata") from the rails console, it substitutes null for idxfield and throws an exception.
For anyone coming across this now, you can use an expression in a default value migration by wrapping it in a lambda: https://github.com/rails/rails/pull/20005
e.g.
create_table :posts do |t|
t.datetime :published_at, default: -> { 'NOW()' }
end
Sounds like you're trying to use Postgres sequences, which rails will use in all primary key definitions in migrations.
If you need to add it to non-primary key column then you will need to run an ALTER TABLE outside of the create_table and add it yourself, like
execute("ALTER TABLE users ADD COLUMN foo INTEGER default nextval('users_id_seq'::regclass)")
I want to create a model 'Relation' which extends ActiveRecord::Base, set it's table name as 'questions_tags', and without primary key. What should I do?
class Relation < ActiveRecord::Base
set_table_name 'questions_tags' # set table name, right?
# how to define 'no-pk'?
end
UPDATE
I know use 'create_table' can solve this problem, but this is just what I want to know: What is the magic behind create_table(:id=>false)? How can I get the same effect without using create_table(:id=>false)?
Create a migration that looks like this:
class CreateQuestionsTags < ActiveRecord::Migration
def self.up
create_table :questions_tags, {:id => false, :force => true} do |t|
...
t.timestamps
end
end
def self.down
drop_table :questions_tags
end
end
If you're looking to create a pivot table, as it looks like from the table name, then AR will handle that in the background.
However, if you're looking to create a table with more feilds then:
1) rename your table to "realtions" please
2) use a primary key "id"
There's no good reason not to be using a primary key in a table, and it is very likely that you might well find yourself regretting it later.
Why don't you want a PK?
Active Record expects a PK, and I don't see what harm it can do.
I created a new model in my rails app. Since it's a one-to-one relation with another table, there's no need for the new model to have an id column. Everything is working fine, but for some reason, all of my Shoulda tests on this model are failing. For example:
should_validate_presence_of :first_name
is throwing this error:
ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'my_new_table.id'
in 'field list': SELECT `my_new_table`.id FROM `my_new_table` WHERE
(`my_new_table`.`some_other_column` IS NULL) LIMIT 1
Does Shoulda require an ID column? If so, is there a way around this?
Did you alias the primary key for Rails purposes?
set_primary_key :my_fk_id
Rails expects your models to have an integer id column as a primary key. You can set it to the foreign key like #MattMcKnight suggests but I'd recommend you create the id column even if you don't strictly need it. In my experience it will save you tons of headaches like this, with basically no downside.
Did you run rake db:migrate and rake db:test:prepare after you created the new model?
All tables require an id column that's just the way that ActiveRecord works. As MattMcKnight points out, you can designate another column as the primary key. You can also specify that a table have no id column is creatied by providing :id => false as an option to create_table.
Matt's answer covers renaming the primary key in the model definition. Here's how to do it in a migration.
Note, providing the primary key option without giving the id option as false means that you do not have to add to column's definition to the block. Rails will create it for you. And automatically use it in any join tables.
Example:
class CreateTableWithOUtID < ActiveRecord::Migration
def self.up
create_table :my_new_table, :primary_key => :another_table_id do |t|
t.string :some_other_column
end
end
def self.down
drop_table, :my_new_table
end
end