Learning to write a rails migration

I am adding a new table to katello and would like to learn how to properly define the migration. Here are the details:

ManifestPlatform (new model)

  • required to reference one ManifestList
  • required to reference one Manifest
  • fields arch, os

ManifestList (existing model)

  • may reference zero or more ManifestPlatform
  • on ManifestList destroy, dependent destroy associated ManifestPlatform

Manifest (existing model)

  • may be referenced by zero or more ManifestPlatform

Common use cases:

  • For a Manifest, show its ManifestPlatforms
  • For a ManifestList, show its ManifestPlatforms
  • Find all ManifestLists with ManifestPlatform.arch == “x86_64”

Here’s what I have so far below. It was based on other migrations but I’d like to know the “why” of the lines and get corrections.

class AddDockerManifestPlatform < ActiveRecord::Migration[5.1]
  def up
    create_table :katello_docker_manifest_platforms do |t|
      t.string :arch, :limit => 255
      t.string :os, :limit => 255
      t.timestamps
    end

    create_table :katello_manifest_manifest_platforms do |t|
      t.references :docker_manifest, :null => false
      t.references :docker_manifest_platform, :null => false
      t.timestamps
    end

    create_table :katello_manifest_list_manifest_platforms do |t|
      t.references :docker_manifest_list, :null => false
      t.references :docker_manifest_platform, :null => false
      t.timestamps
    end

    add_index :katello_docker_manifest_manifest_platforms, [:docker_manifest_id, :docker_manifest_platform_id],
              :name => :katello_docker_manifest_manifest_platform_id

    add_index :katello_docker_manifest_list_manifest_platforms, [:docker_manifest_list_id, :docker_manifest_platform_id],
              :name => :katello_docker_manifest_list_manifest_platform_id, :unique => true

    add_foreign_key :katello_docker_manifest_manifest_platforms, :katello_docker_manifest_platforms,
                    :column => :docker_manifest_platform_id

    add_foreign_key :katello_docker_manifest_list_manifest_platforms, :katello_docker_manifest_platforms,
                    :column => :docker_manifest_platform_id
  end
end

The names are also very long and end up violating the 63 character limit. Guidance on that would be welcome.

I’ll just explain the things after the table definitions, as those seem straightforward.

    add_index :katello_docker_manifest_manifest_platforms, [:docker_manifest_id, :docker_manifest_platform_id],
              :name => :katello_docker_manifest_manifest_platform_id

Adds a database index on these two columns. Indexes are used to speed up queries as well as ensure uniqueness. I would add :unique => true here since we don’t want duplicates. The index names are autogenerated by rails, but a lot of the time (as you discovered) are too big for the database so you have to specify your own. Normally we abbreviate the column names to make them fit. For example the auto generated name would be something like:

katello_docker_manifest_manifest_platforms_docker_manifest_id_docker_manifest_platform_id

We might abbreviate that to TableName-ColumnAbbrev1-ColumnAbrrev2, for example:

:name => 'katello_docker_manifest_manifest_platforms_dm_id_dmp_id'

It may be that at some point we need to abbreviate the table name as well.

    add_index :katello_docker_manifest_list_manifest_platforms, [:docker_manifest_list_id, :docker_manifest_platform_id],
              :name => :katello_docker_manifest_list_manifest_platform_id, :unique => true

This one looks good, but i would use the name abbreviation as above.

    add_foreign_key :katello_docker_manifest_manifest_platforms, :katello_docker_manifest_platforms,
                    :column => :docker_manifest_platform_id

These add foreign key contraints to the table, which means that if you insert a record into the table, this line makes sure that for your given docker_manifest_platform_id, there exists some record in katello_docker_manifest_manifest_platforms with that id. Its there to ensure data integrity. You may need to specify a :name, like the indexes if the auto-generated value is too big.

    add_foreign_key :katello_docker_manifest_list_manifest_platforms, :katello_docker_manifest_platforms,
                    :column => :docker_manifest_platform_id
  end
end

Looks like you’re missing a foreign key for each join table. Any time you reference another table you want a foreign key defined to ensure the data integrity.

Does that help? Any other specific questions?

I’ve created a branch with the migration.

Next I need to also figure out what what how the models should be updated.

Migration: https://github.com/thomasmckay/katello/blob/f39461e59c6113e36a5bd93d5223c0fc51082027/db/migrate/20180723105200_add_docker_manifest_platform.rb

Branch: https://github.com/Katello/katello/compare/master...thomasmckay:24357-arch-os-size