How to change primary key in rails migration file? - ruby-on-rails

I need to migrate an old mysql table like this:
Products
name (string, primary_key)
to this schema:
Products
id (integer, primary_key, auto_generated)
name (unique)
I need the Products.id values populated in the new table.
How can i write the rails migration file? I am using Rails 3.2.7
I have 2 problems now:
1. I can't find a method to remove primary key in ActiveRecord::Migration
2. I don't know how to generate values for newly added primary key.

You could execute arbitrary SQL in your migration:
execute "ALTER TABLE `products` DROP PRIMARY KEY"
and then add the new column:
add_column :products, :id, :primary_key
See:
Remove Primary Key in MySQL
how to add a primary key to a table in rails
http://thinkwhere.wordpress.com/2009/05/09/adding-a-primary-key-id-to-table-in-rails/
http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

If you're on Postgresql, the syntax is slightly different.
ALTER TABLE <table_name> DROP CONSTRAINT <table_name>_pkey;

Related

Hybrid composite key in rails - using jsonb field and db column

I need to add a composite primary key to my project but the issue i'm facing is that one of the fields i want to use is in a jsonb field, and the other key is a regular foreign key.
Is it somehow possible for me to set up a hybrid composite key index?
Something like this:
add_index :users, [:phone_number, :department_id], unique: true
Where phone_number is stored in a jsonb field
meta_data: {phone_number: "987654321"}

How to update a table which doesn't have primary id column in rails

I am dealing with legacy code and do not have access to the database. There is a table which does not have a primary id column.
I can find a record using record = Model.find_or_initialize_by(listing_id: rating.pid, criteria_id: 33).
I can increment an attribute with record.rating_count += 1 but when I try to save it with record.save!, it gives the error TypeError: nil is not a symbol nor a string.
I think this is because record does not have a primary id (key) but I am not sure why it doesn't update the record.
If you have any suggestions, please let me know.
I also received the error TypeError: nil is not a symbol nor a string when trying to update a model which has no id column (or other primary key I could use). I had to add one.
You can add a primary key retrospectively using a migration:
add_column :people, :id, :primary_key

on heroku with rails / postgres, uid strings are converted to integers

See this answer for how I implemented string primary keys in my rails app: https://stackoverflow.com/a/1434819/4585520
In the migrations, id: false is specified and some custom postgres execution code is added (i.e. execute "ALTER TABLE users ADD PRIMARY KEY (uid);" where uid is a string). Also, in the models, self.primary_key = :uid is usedThe result is that I have no 'id' columns, just 'uid' columns which are all strings.
However, when I deploy this to Heroku and run my seed generation, I get errors because of duplicate uids. I notice that the added uid values are integers. Perhaps the string values I'm providing are being converted to integers (I have UIDs in a JSON file and am importing those, so no uid generation is occurring), which would explain why they're producing duplicate values.
Why are my uids integers on heroku when they're correctly running as strings in development?
I don't believe this is a Heroku issue, but rather an issue with how the migration tweaked the database.
I've got a Rails app that uses UUID's for primary keys as well. Here's a look at my DB on Heroku Postgres:
Column | Type | Modifiers
------------+-----------------------------+-------------------------------------------
id | uuid | not null default uuid_generate_v4()
data | json | not null
Here was my migration:
enable_extension 'uuid-ossp'
create_table :notifications, id: :uuid do |t|
t.json :data, null: false
end
I'm guessing that the column type may not have been set correctly in your migration. Take a look at your DB on heroku (heroku pg:psql) -- is the uid column an integer that's using a sequence? Do you see something like nextval('users_uid_seq'::regclass)? If so, this requires the column to be an integer. I'd try the following migration to modify your database:
enable_extension 'uuid-ossp'
remove_column :users, :uid
add_column :users, :uuid, :uuid, default: 'uuid_generate_v4()'
This will:
ensure that UUID generation for PG is enabled
remove the current uid column (just to get proper naming)
create a new uuid column, with the type set as uuid and the generation handled on the PG side.
If you're already using Rails to generate UUID's (not recommended, though), you could just go into your database and change the column type from integer to string.
I hope that helps!

Defining table compound primary keys in rails for Cassandra

Given the following pseudo-cql table structure:
CREATE TABLE searches (
category text,
timestamp timestamp,
no_of_searches int,
avg_searches double,
PRIMARY KEY((category, timestamp), no_of_searches)
);
and the following Rails Cequel model:
class Search
include Cequel::Record
# Table columns
key :category, :text
key :timestamp, :timestamp
key :no_of_searches, :int
column :avg_searches, :double
end
when I try to synchronise the model using:
rake cequel:migrate
the following rake error is thrown:
rake aborted!
Cequel::InvalidSchemaMigration: Existing partition keys category,timestamp differ from specified partition keys timestamp
I'm trying to get the above rails model to synchronise with the above table using the partition keys, although it's stating that the two sets of keys are different. I've tried defining the keys in the same order, but has not worked.
My objective is to get the pre-defined database table with partition keys working with the rails model. Any help would be grateful!
The key method supports an options hash as a third parameter. The options hash adds further support to the defined key such as order and partitioning.
Based upon the given table definition it would mean your table columns would look something like this:
# Table columns
key :category, :text, { partition: true }
key :timestamp, :timestamp, { partition: true }
key :no_of_searches, :int
column :avg_searches, :double
Unfortunately this is not documented within the Cequel README, however it is documented within the code, for which can be found here.

Set Primary Key value 0 and auto increment on Migration PostgreSQL

I have a model with 2 fields => :name and :age
I need to do a Migration that add a column :position which needs auto increment and start with 0 (zero).
I tried these way:
class AddPosition < ActiveRecord::Migration
def up
add_column :clientes, :position, :integer, :default => 0, :null => false
execute "ALTER TABLE clientes ADD PRIMARY KEY (position);"
end
But it doesn't work because it not auto increment. If I try to use primary key as type:
class AddPosition < ActiveRecord::Migration
def up
add_column :clientes, :position, :primary_key, :default => 0, :null => false
end
end
rake db:migrate don't run because multiple values.
Anyone could explain a way to have zeros and autoincrement on Primary Key w/ Rails 3.2?
Here's how you can set up auto increment column in PostgreSQL:
# in migration:
def up
execute <<-SQL
CREATE SEQUENCE clients_position_seq START WITH 0 MINVALUE 0;
ALTER TABLE clients ADD COLUMN position INTEGER NOT NULL DEFAULT nextval('clients_position_seq');
SQL
end
But unfortunately it may not be what you need. The above would work if you'd insert values into clients table with SQL like this: INSERT INTO clients(name, age) VALUES('Joe', 21), and rails doesn't work that way.
The first problem is that rails expects primary key to be called id. And while you can override this convention, it would cause more problems than it would solve. If you want to bind position to primary key value, better option is to add virtual attribute to your model, a method like this:
def position
id.nil? ? nil : id - 1
end
But let's suppose you already have conventional primary key id and want to add position field so that you can reorder clients after they have been created, without touching their ids (which is always a good idea). Here comes the second problem: rails won't recognize and respect DEFAULT nextval('clients_position_seq'), i.e. it won't pull values from PG backed sequence and would try to put NULL in position by default.
I'd like to suggest looking at acts_as_list gem as better option. This would make DB sequence manipulations unnecessary. Unfortunately it uses 1 as initial value for position but that can be cured by setting custom name for list position field and defining method as I showed above.

Resources