Rails 4. Migrate table id to UUID

后端 未结 3 741
予麋鹿
予麋鹿 2020-12-05 18:39

I have a table: db/migrate/20140731201801_create_voc_brands.rb:

class CreateVocBrands < ActiveRecord::Migration
  def change
    create_table :vo         


        
3条回答
  •  刺人心
    刺人心 (楼主)
    2020-12-05 19:17

    I know this doesn't directly answer the question, but I created a rake task that can help convert any project from id to uuid https://gist.github.com/kuczmama/152d762177968f7192df1dea184e3370

    task id_to_uuid: :environment do
      puts "[START] Convert id to uuid"
      ActiveRecord::Base.connection.enable_extension 'uuid-ossp' unless ActiveRecord::Base.connection.extensions.include? 'uuid-ossp'
      ActiveRecord::Base.connection.enable_extension 'pgcrypto' unless ActiveRecord::Base.connection.extensions.include? 'pgcrypto'
    
      table_names = ActiveRecord::Base.connection.tables - ["schema_migrations", "ar_internal_metadata", "migration_validators"]
      table_names.each do |table_name|
        puts "[CREATE] uuid column for #{table_name}"
    
        #Make sure the column is a uuid if not delete it and then create it
        if ActiveRecord::Migration.column_exists? table_name, :uuid
          column_type = ActiveRecord::Migration.columns(table_name).select{|c| c.name == "uuid"}.try(:first).try(:sql_type_metadata).try(:type)
          if column_type && column_type != :uuid
            ActiveRecord::Migration.remove_column(table_name, :uuid)
          end
        end
    
        # Create it if it doesn't exist
        if !ActiveRecord::Migration.column_exists? table_name, :uuid
          ActiveRecord::Migration.add_column table_name, :uuid, :uuid, default: "uuid_generate_v4()", null: false
        end
    
      end
    
      # The strategy here has three steps.
      # For each association:
      # 1) write the association's uuid to a temporary foreign key _uuid column,
      # 2) For each association set the value of the _uuid column
      # 3) remove the _id column and
      # 4) rename the _uuid column to _id, effectively migrating our foreign keys to UUIDs while sticking with the _id convention.
      table_names.each do |table_name|
        puts "[UPDATE] change id to uuid #{table_name}"
        model = table_name.singularize.camelize.constantize
        id_columns = model.column_names.select{|c| c.end_with?("_id")}
    
    
        # write the association's uuid to a temporary foreign key _uuid column
        # eg. Message.room_id => Message.room_uuid
        model.reflections.each do|k, v|
          begin
            association_id_col = v.foreign_key
            # Error checking
            # Make sure the relationship actually currently exists
            next unless id_columns.include?(association_id_col)
            # Check that there is at
    
            # 1) Create temporary _uuid column set to nulll,
            tmp_uuid_column_name = column_name_to_uuid(association_id_col)
            unless ActiveRecord::Migration.column_exists?(table_name, tmp_uuid_column_name)
              puts "[CREATE] #{table_name}.#{tmp_uuid_column_name}"
              ActiveRecord::Migration.add_column(table_name, tmp_uuid_column_name, :uuid)
            end
    
            # 2) For each association set the value of the _uuid column
            #
            # For example.  Assume the following example
            #
            # message.room_id = 1
            # room = Room.find(1)
            # room.uuid = 0x123
            # message.room_uuid = 0x123
            #
            association_klass = v.klass
    
            model.unscoped.all.each do |inst|
              next unless inst.present?
              association = association_klass.find_by(id: inst.try(association_id_col.try(:to_sym)))
              next unless association.present?
              inst.update_column(tmp_uuid_column_name, association.try(:uuid))
            end
    
            # 3) Remove id column
            ActiveRecord::Migration.remove_column table_name, association_id_col if ActiveRecord::Migration.column_exists?(table_name, association_id_col)
    
            # 4) Rename uuid_col_name to id
            ActiveRecord::Migration.rename_column table_name, tmp_uuid_column_name, association_id_col
          rescue => e
            puts "Error: #{e} continuing"
            next
          end
        end
    
        # Make each temp _uuid column linked up
        # eg. Message.find(1).room_uuid = Message.find(1).room.uuid
        puts "[UPDATE] #{model}.uuid to association uuid"
      end
    
      ## Migrate primary keys to uuids
      table_names.each do |table_name|
        if ActiveRecord::Migration.column_exists?(table_name, :id) && ActiveRecord::Migration.column_exists?(table_name, :uuid)
          ActiveRecord::Base.connection.execute %Q{ALTER TABLE #{table_name} DROP CONSTRAINT #{table_name}_pkey CASCADE} rescue nil
          ActiveRecord::Migration.remove_column(table_name, :id)
          ActiveRecord::Migration.rename_column( table_name, :uuid, :id) if ActiveRecord::Migration.column_exists?(table_name, :uuid)
          ActiveRecord::Base.connection.execute "ALTER TABLE #{table_name} ADD PRIMARY KEY (id)"
          ActiveRecord::Base.connection.execute %Q{DROP SEQUENCE IF EXISTS #{table_name}_id_seq CASCADE} rescue nil
        end
      end
    end
    
    # Add uuid to the id
    # EG. column_name_to_uuid("room_id") => "room_uuid"
    # EG. column_name_to_uuid("room_ids") => "room_uuids"
    def column_name_to_uuid(column_name)
      *a, b = column_name.split("_id", -1)
      a.join("_id") + "_uuid" + b
    end
    

提交回复
热议问题