Порядок и отношения файла миграции базы данных Heroku Rails

У меня есть модель User и модель Role. Когда я начал создавать приложение, я начал с создания модели User, а созданный файл миграции содержит ссылку на роли:

class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :first_name
      t.string :last_name
      t.string :username
      t.string :email
      t.string :password
      t.string :password_digest
      t.boolean :banned
      t.references :role, foreign_key: true

      t.timestamps
    end
  end
end

Затем я создал модель Role, которая сгенерировала этот файл миграции:

class CreateRoles < ActiveRecord::Migration[5.0]
  def change
    create_table :roles do |t|
      t.string :title
      t.integer :access_level

      t.timestamps
    end
  end
end

Я пытаюсь выполнить развертывание в Heroku и перенести свою базу данных в соответствии с документацией, используя следующую команду heroku run rails db:migrate (используя Rails 5).

Я получаю сообщение об ошибке от Heroku:

heroku run rake db:migrate
Running rake db:migrate on ⬢ gentle-headland-79177... up, run.9293 (Free)
D, [2016-12-31T08:15:33.131367 #4] DEBUG -- :    (90.7ms)  CREATE TABLE "schema_migrations" ("version" character varying PRIMARY KEY)
D, [2016-12-31T08:15:33.152682 #4] DEBUG -- :    (11.5ms)  CREATE TABLE "ar_internal_metadata" ("key" character varying PRIMARY KEY, "value" character varying, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
D, [2016-12-31T08:15:33.155373 #4] DEBUG -- :    (1.1ms)  SELECT pg_try_advisory_lock(6845940114126317925);
D, [2016-12-31T08:15:33.172106 #4] DEBUG -- :   ActiveRecord::SchemaMigration Load (1.2ms)  SELECT "schema_migrations".* FROM "schema_migrations"
I, [2016-12-31T08:15:33.178453 #4]  INFO -- : Migrating to CreateUsers (20161117083901)
D, [2016-12-31T08:15:33.181903 #4] DEBUG -- :    (0.9ms)  BEGIN
== 20161117083901 CreateUsers: migrating ======================================
-- create_table(:users)
D, [2016-12-31T08:15:33.199351 #4] DEBUG -- :    (13.4ms)  CREATE TABLE "users" ("id" serial primary key, "first_name" character varying, "last_name" character varying, "username" character varying, "email" character varying, "password" character varying, "password_digest" character varying, "banned" boolean, "role_id" integer, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL, CONSTRAINT "fk_rails_642f17018b"
FOREIGN KEY ("role_id")
  REFERENCES "roles" ("id")
)
D, [2016-12-31T08:15:33.200707 #4] DEBUG -- :    (1.0ms)  ROLLBACK
D, [2016-12-31T08:15:33.202190 #4] DEBUG -- :    (1.2ms)  SELECT pg_advisory_unlock(6845940114126317925)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

PG::UndefinedTable: ERROR:  relation "roles" does not exist
: CREATE TABLE "users" ("id" serial primary key, "first_name" character varying, "last_name" character varying, "username" character varying, "email" character varying, "password" character varying, "password_digest" character varying, "banned" boolean, "role_id" integer, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL, CONSTRAINT "fk_rails_642f17018b"
FOREIGN KEY ("role_id")
  REFERENCES "roles" ("id")
)

Насколько я понимаю, Heroku ожидает, что сначала будет определено Role, а затем User.

Почему на моей локальной машине я могу выполнить db:migrate нормально, но на Heroku это не удается?

Возможно, разница между Sqlite3 и Postgresql?

Как решить эту проблему развертывания?

Мне просто переименовать мой файл миграции create_role, чтобы он имел более раннюю отметку времени, чем файл миграции create_user? Это даже рекомендуемая практика? :D

Обновлять

Я сделал git-клон своего репозитория в папку на рабочем столе на своем iMac.

Затем я запустил rails db:migrate на этой новой локальной копии.

Никакой ошибки. Все миграции БД выполнены, все таблицы на месте вместе со всеми отношениями. Что-то действительно не так на конце Heroku.

Обновление 2

Сделал еще одну проверку моего репозитория в новую папку на рабочем столе, запустил bundle install, а затем попробовал эту версию команды db: migrate:

rails db:migrate RAILS_ENV=production

и я вижу тот же вывод ошибки о ролях не существует

ТЕМ НЕ МЕНИЕ

Затем я создал новый проект рельсов honey:

rails new honey

Сделал bundle install

Затем пошел:

rails generate model User name:string role:references

Наконец я пошел:

rails db:migrate RAILS_ENV=production

и ошибок нет...

Я явно не сгенерировал никакую модель Role, так почему же она не дает сбоев?

Вот лог консоли:

Warlocks-iMac:bad clementwu$ cd honey/
Warlocks-iMac:honey clementwu$ ls
Gemfile      Rakefile     config       lib          test
Gemfile.lock app          config.ru    log          tmp
README.md    bin          db           public       vendor
Warlocks-iMac:honey clementwu$ rails generate model User name:string role:references
Running via Spring preloader in process 27200
Expected string default value for '--jbuilder'; got true (boolean)
      invoke  active_record
      create    db/migrate/20170101100613_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
Warlocks-iMac:honey clementwu$ rails db:migrate RAILS_ENV=production
== 20170101100613 CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0018s
== 20170101100613 CreateUsers: migrated (0.0018s) =============================

Warlocks-iMac:honey clementwu$ 

скриншот

База данных создана и даже показывает внешний ключ role_id, несмотря на отсутствие таблицы с именем role в моей производственной базе данных:

скриншот базы данных

Сбивает с толку :D

Обновление 3

Возможно, это разница между базой данных sqlite3 и базой данных postgresql.

По умолчанию приложение rails config/database.yml указывает, что производственная база данных называется db/production.sqlite3, т. е. она не использует базу данных PostgreSQL, поэтому она не выдает ошибку о том, что роли не существуют.

И согласно этому сообщению Stackoverflow: Поддерживает ли SQLite ссылочную целостность?

Похоже, что SQLite3 не гарантирует ссылочную целостность :(

Сильная боль в ягодицах.

Хорошо, что это только личный проект обучения, а не рабочий проект.

Также нереально начинать с PostgreSQL, вы не можете просто удалить и воссоздать базу данных так же легко, как с SQLite3 и Rails CLI.

Я предполагаю, что урок, который нужно усвоить здесь, заключается в том, чтобы хорошенько подумать и сначала создать зависимые таблицы.


person Zhang    schedule 31.12.2016    source источник
comment
Мне просто переименовать мой файл миграции create_role, чтобы он имел более раннюю отметку времени, чем файл миграции create_user? — точно.   -  person 31piy    schedule 31.12.2016
comment
Переименование, git commit и git push heroku master исправляет первую ошибку, но затем появляется еще одна ошибка, говорящая о том, что отношения авторов книг (типа модели User) не существует. Я думаю проблема кроется где-то глубже. Поэтому, основываясь на этом результате, я не думаю, что переименование файла миграции — это правильный путь.   -  person Zhang    schedule 31.12.2016


Ответы (3)


Мне просто переименовать мой файл миграции create_role, чтобы он имел более раннюю отметку времени, чем файл миграции create_user? Это даже рекомендуемая практика? :D

Да, в вашем случае нормально.

НО имейте в виду, что это нормально на шагах, когда вы формируете систему в самом начале. Если у вас есть производственная база данных, вы должны быть уверены, что ваши миграции выполняются успешно перед развертыванием.

Так что просто совет на будущее, здесь моя собственная лучшая практика развертывания производства

  1. Сделайте дамп вашей производственной базы данных. Вы можете сделать это с

    pg_dump <db_name> > <dump_file>; scp <server>:<path_to_dump_file> ./

    или https://github.com/sgruhier/capistrano-db-tasks

    в случае с героку вот хорошая статья

  2. Примените его в локальной среде с помощью

    psql <db_name> < <dump_file>

  3. Попробуйте запустить миграцию

  4. Если все пойдет не так, перейдите к шагу 5, иначе к шагу 6.

  5. Устранение проблем с миграцией перейдите к шагу 4

  6. Убедитесь, что миграция не повреждает данные, и если она пытается исправить миграцию, и начните с шага 2, в противном случае просто разверните свой код;)

person mpospelov    schedule 31.12.2016
comment
Хороший контрольный список. Однако он уже упомянул, что миграция прошла нормально на его локальной машине. - person 31piy; 31.12.2016
comment
Проблема в том, что среда может отличаться, я думаю, что это невозможно запустить даже на локальной машине, так как add_references добавляет внешний ключ по умолчанию, но это не может быть продолжено, пока не будут созданы упомянутые таблицы. Другой способ - ссылаться на таблицу в отдельных миграциях. - person mpospelov; 31.12.2016
comment
@mixan946 даже после переименования моего файла миграции, поэтому создание роли происходит до создания таблицы пользователя, я получаю новую ошибку, говорящую о том, что файл миграции создания книги не может ссылаться на автора (автор является ссылкой на таблицу пользователя и, что более важно, файл миграции пользователя уже запущен до создания книги файл миграции, как он может не найти отношение?). - person Zhang; 31.12.2016
comment
Это первое развертывание на Heroku, зачем мне производственная база данных, разве это не то, что я пытаюсь создать в данный момент, запустив heroku run rails db:migrate? - person Zhang; 31.12.2016
comment
Ваши шаги также кажутся парадоксальными: Шаг 4 - миграция идет не так, переходите к шагу 5, Шаг 5 исправляет проблему миграции, переходите к шагу 4, лол, бесконечный цикл. Шаг 6, убедитесь, что миграция не повреждает данные, исправьте миграцию, если она повреждена... что..? - person Zhang; 31.12.2016

Я исправил все свои миграции и теперь API работает на сервере Heroku.

Правильный ответ: развертывать на Heroku как можно раньше, не дожидаясь окончания разработки на локальной машине, а затем развертывать.

Раннее развертывание позволит выявить проблемы на ранней стадии, такие как несоответствия миграции между SQlite и PostgreSQL.

Кроме того, при выполнении ссылок в файле миграции, если не используются совпадающие имена модели и таблицы, например. author против user, измените файл миграции, чтобы использовать add_foreign_key перед запуском миграции.

Например:

class CreateBooks < ActiveRecord::Migration[5.0]
  def change
    create_table :books do |t|
      t.string :title
      t.boolean :adult_content
      t.references :author, foreign_key: true

      t.timestamps
    end
  end
end

Должен стать:

class CreateBooks < ActiveRecord::Migration[5.0]
  def change
    create_table :books do |t|
      t.string :title
      t.boolean :adult_content
      t.references :author, index: true # this line changed

      t.timestamps
    end

    # new foreign key specifying correct table name and column
    add_foreign_key :books, :users, column: :author_id 

  end
end

Нашел новые знания по этой ссылке:

http://sevenseacat.net/2015/02/24/add_foreign_key_gotchas.html

person Zhang    schedule 02.01.2017

Миграция с SQLite на PostgreSQl для отправки на героку.

У меня такая ситуация, но с использованием таблиц: посещаемость, события и пользователи.

PG:undifiedTable ОШИБКА: отношение пользователей не существует

Я решил это:

  1. Локально я запускаю rails db:drop — для локального удаления баз данных

    • I had 3 migrations file example
    • 2019_11_07_205648_create_attendace
    • 2019_11_07_215648_create_events
    • 2019_11_07_225648_create_users
  2. Я меняю день в дате в первых двух файлах на:

    • 2019_11_07_225648_create_users
    • 2019_11_07_235648_create_events
    • 2019_11_07_245648_create_attendace
  3. Таким образом, они меняют порядок, и сначала создается таблица пользователей, я создал базы данных и снова выполнил миграцию $rails db:create rails db:migrate — работает локально

  4. push to heroku после коммита - git push heroku master

  5. heroku run rails db:migrate

  6. Открыть Heroku и готово - Вуаля - Финито

person Gonza    schedule 05.06.2020