diff options
author | Douwe Maan <douwe@gitlab.com> | 2018-11-06 15:43:24 +0000 |
---|---|---|
committer | Douwe Maan <douwe@gitlab.com> | 2018-11-06 15:43:24 +0000 |
commit | 1208d55206128266690f46f0165df0fc10c24941 (patch) | |
tree | 93fbdde5a5db6cdd8f79f2806707dab093985aa2 /db | |
parent | d171ff60168cd55b6d7b9ee920269f44a26e577e (diff) | |
parent | d0c58a97c8a053c0beec7c13c1c6ec5042120ef1 (diff) | |
download | gitlab-ce-1208d55206128266690f46f0165df0fc10c24941.tar.gz |
Merge branch 'master' into 'refactor-snippets-finder'refactor-snippets-finder
# Conflicts:
# spec/models/project_spec.rb
Diffstat (limited to 'db')
16 files changed, 284 insertions, 62 deletions
diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb index 285436f4324..7a86fe2eb7c 100644 --- a/db/fixtures/development/17_cycle_analytics.rb +++ b/db/fixtures/development/17_cycle_analytics.rb @@ -180,11 +180,8 @@ class Gitlab::Seeder::CycleAnalytics ref: "refs/heads/#{merge_request.source_branch}") pipeline = service.execute(:push, ignore_skip_ci: true, save_on_errors: false) - pipeline.run! - Timecop.travel rand(1..6).hours.from_now - pipeline.succeed! - - PipelineMetricsWorker.new.perform(pipeline.id) + pipeline.builds.map(&:run!) + pipeline.update_status end end @@ -204,7 +201,8 @@ class Gitlab::Seeder::CycleAnalytics job = merge_request.head_pipeline.builds.where.not(environment: nil).last - CreateDeploymentService.new(job).execute + job.success! + pipeline.update_status end end end diff --git a/db/migrate/20180413022611_create_missing_namespace_for_internal_users.rb b/db/migrate/20180413022611_create_missing_namespace_for_internal_users.rb index 8fc558be733..b7b346cb10e 100644 --- a/db/migrate/20180413022611_create_missing_namespace_for_internal_users.rb +++ b/db/migrate/20180413022611_create_missing_namespace_for_internal_users.rb @@ -45,7 +45,7 @@ class CreateMissingNamespaceForInternalUsers < ActiveRecord::Migration connection.exec_query(query).present? end - insert_query = "INSERT INTO namespaces(owner_id, path, name) VALUES(#{user_id}, '#{path}', '#{path}')" + insert_query = "INSERT INTO namespaces(owner_id, path, name, created_at, updated_at) VALUES(#{user_id}, '#{path}', '#{path}', NOW(), NOW())" namespace_id = connection.insert_sql(insert_query) create_route(namespace_id) @@ -57,7 +57,7 @@ class CreateMissingNamespaceForInternalUsers < ActiveRecord::Migration row = connection.exec_query("SELECT id, path FROM namespaces WHERE id=#{namespace_id}").first id, path = row.values_at('id', 'path') - execute("INSERT INTO routes(source_id, source_type, path, name) VALUES(#{id}, 'Namespace', '#{path}', '#{path}')") + execute("INSERT INTO routes(source_id, source_type, path, name, created_at, updated_at) VALUES(#{id}, 'Namespace', '#{path}', '#{path}', NOW(), NOW())") end def set_notification_email(user_id) diff --git a/db/migrate/20180927073410_add_index_to_project_deploy_tokens_deploy_token_id.rb b/db/migrate/20180927073410_add_index_to_project_deploy_tokens_deploy_token_id.rb new file mode 100644 index 00000000000..61d32fe16eb --- /dev/null +++ b/db/migrate/20180927073410_add_index_to_project_deploy_tokens_deploy_token_id.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class AddIndexToProjectDeployTokensDeployTokenId < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + # MySQL already has index inserted + add_concurrent_index :project_deploy_tokens, :deploy_token_id if Gitlab::Database.postgresql? + end + + def down + remove_concurrent_index(:project_deploy_tokens, :deploy_token_id) if Gitlab::Database.postgresql? + end +end diff --git a/db/migrate/20181015155839_add_finished_at_to_deployments.rb b/db/migrate/20181015155839_add_finished_at_to_deployments.rb new file mode 100644 index 00000000000..1a061bb0f5f --- /dev/null +++ b/db/migrate/20181015155839_add_finished_at_to_deployments.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddFinishedAtToDeployments < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + add_column :deployments, :finished_at, :datetime_with_timezone + end + + def down + remove_column :deployments, :finished_at, :datetime_with_timezone + end +end diff --git a/db/migrate/20181016141739_add_status_to_deployments.rb b/db/migrate/20181016141739_add_status_to_deployments.rb new file mode 100644 index 00000000000..321172696b4 --- /dev/null +++ b/db/migrate/20181016141739_add_status_to_deployments.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class AddStatusToDeployments < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DEPLOYMENT_STATUS_SUCCESS = 2 # Equivalent to Deployment.state_machine.states['success'].value + + DOWNTIME = false + + disable_ddl_transaction! + + ## + # NOTE: + # Ideally, `status` column should not have default value because it should be leveraged by state machine (i.e. application level). + # However, we have to use the default value for avoiding `NOT NULL` violation during the transition period. + # The default value should be removed in the future release. + def up + add_column_with_default(:deployments, + :status, + :integer, + limit: 2, + default: DEPLOYMENT_STATUS_SUCCESS, + allow_null: false) + end + + def down + remove_column(:deployments, :status) + end +end diff --git a/db/migrate/20181019032400_add_shards_table.rb b/db/migrate/20181019032400_add_shards_table.rb new file mode 100644 index 00000000000..5e0a6960548 --- /dev/null +++ b/db/migrate/20181019032400_add_shards_table.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class AddShardsTable < ActiveRecord::Migration + DOWNTIME = false + + def change + create_table :shards do |t| + t.string :name, null: false, index: { unique: true } + end + end +end diff --git a/db/migrate/20181019032408_add_repositories_table.rb b/db/migrate/20181019032408_add_repositories_table.rb new file mode 100644 index 00000000000..077f264d3ce --- /dev/null +++ b/db/migrate/20181019032408_add_repositories_table.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddRepositoriesTable < ActiveRecord::Migration + DOWNTIME = false + + def change + create_table :repositories, id: :bigserial do |t| + t.references :shard, null: false, index: true, foreign_key: { on_delete: :restrict } + t.string :disk_path, null: false, index: { unique: true } + end + + add_column :projects, :pool_repository_id, :bigint + add_index :projects, :pool_repository_id, where: 'pool_repository_id IS NOT NULL' + end +end diff --git a/db/migrate/20181019105553_add_projects_pool_repository_id_foreign_key.rb b/db/migrate/20181019105553_add_projects_pool_repository_id_foreign_key.rb new file mode 100644 index 00000000000..059988de38a --- /dev/null +++ b/db/migrate/20181019105553_add_projects_pool_repository_id_foreign_key.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class AddProjectsPoolRepositoryIdForeignKey < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_foreign_key( + :projects, + :repositories, + column: :pool_repository_id, + on_delete: :nullify + ) + end + + def down + remove_foreign_key(:projects, column: :pool_repository_id) + end +end diff --git a/db/migrate/20181022135539_add_index_on_status_to_deployments.rb b/db/migrate/20181022135539_add_index_on_status_to_deployments.rb new file mode 100644 index 00000000000..2eed20aa855 --- /dev/null +++ b/db/migrate/20181022135539_add_index_on_status_to_deployments.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddIndexOnStatusToDeployments < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index :deployments, [:project_id, :status] + add_concurrent_index :deployments, [:environment_id, :status] + end + + def down + remove_concurrent_index :deployments, [:project_id, :status] + remove_concurrent_index :deployments, [:environment_id, :status] + end +end diff --git a/db/migrate/20181023104858_add_archive_builds_duration_to_application_settings.rb b/db/migrate/20181023104858_add_archive_builds_duration_to_application_settings.rb new file mode 100644 index 00000000000..744748b3fad --- /dev/null +++ b/db/migrate/20181023104858_add_archive_builds_duration_to_application_settings.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class AddArchiveBuildsDurationToApplicationSettings < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + add_column(:application_settings, :archive_builds_in_seconds, :integer, allow_null: true) + end +end diff --git a/db/migrate/20181023144439_add_partial_index_for_legacy_successful_deployments.rb b/db/migrate/20181023144439_add_partial_index_for_legacy_successful_deployments.rb new file mode 100644 index 00000000000..5896102af1c --- /dev/null +++ b/db/migrate/20181023144439_add_partial_index_for_legacy_successful_deployments.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class AddPartialIndexForLegacySuccessfulDeployments < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + INDEX_NAME = 'partial_index_deployments_for_legacy_successful_deployments'.freeze + + disable_ddl_transaction! + + def up + add_concurrent_index(:deployments, :id, where: "finished_at IS NULL AND status = 2", name: INDEX_NAME) + end + + def down + remove_concurrent_index_by_name(:deployments, INDEX_NAME) + end +end diff --git a/db/migrate/20181031190559_drop_gcp_clusters_table.rb b/db/migrate/20181031190559_drop_gcp_clusters_table.rb deleted file mode 100644 index 808d474b4fc..00000000000 --- a/db/migrate/20181031190559_drop_gcp_clusters_table.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -class DropGcpClustersTable < ActiveRecord::Migration - include Gitlab::Database::MigrationHelpers - - DOWNTIME = false - - def up - drop_table :gcp_clusters - end - - def down - create_table :gcp_clusters do |t| - # Order columns by best align scheme - t.references :project, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade } - t.references :user, foreign_key: { on_delete: :nullify } - t.references :service, foreign_key: { on_delete: :nullify } - t.integer :status - t.integer :gcp_cluster_size, null: false - - # Timestamps - t.datetime_with_timezone :created_at, null: false - t.datetime_with_timezone :updated_at, null: false - - # Enable/disable - t.boolean :enabled, default: true - - # General - t.text :status_reason - - # k8s integration specific - t.string :project_namespace - - # Cluster details - t.string :endpoint - t.text :ca_cert - t.text :encrypted_kubernetes_token - t.string :encrypted_kubernetes_token_iv - t.string :username - t.text :encrypted_password - t.string :encrypted_password_iv - - # GKE - t.string :gcp_project_id, null: false - t.string :gcp_cluster_zone, null: false - t.string :gcp_cluster_name, null: false - t.string :gcp_machine_type - t.string :gcp_operation_id - t.text :encrypted_gcp_token - t.string :encrypted_gcp_token_iv - end - end -end diff --git a/db/migrate/20181101144347_add_index_for_stuck_mr_query.rb b/db/migrate/20181101144347_add_index_for_stuck_mr_query.rb new file mode 100644 index 00000000000..5d3ace54e5c --- /dev/null +++ b/db/migrate/20181101144347_add_index_for_stuck_mr_query.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true +class AddIndexForStuckMrQuery < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index :merge_requests, [:id, :merge_jid], where: "merge_jid IS NOT NULL and state = 'locked'" + end + + def down + remove_concurrent_index :merge_requests, [:id, :merge_jid], where: "merge_jid IS NOT NULL and state = 'locked'" + end +end diff --git a/db/post_migrate/20181022173835_enqueue_populate_cluster_kubernetes_namespace.rb b/db/post_migrate/20181022173835_enqueue_populate_cluster_kubernetes_namespace.rb new file mode 100644 index 00000000000..f80a2aa6eac --- /dev/null +++ b/db/post_migrate/20181022173835_enqueue_populate_cluster_kubernetes_namespace.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class EnqueuePopulateClusterKubernetesNamespace < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + MIGRATION = 'PopulateClusterKubernetesNamespaceTable'.freeze + + disable_ddl_transaction! + + def up + BackgroundMigrationWorker.perform_async(MIGRATION) + end + + def down + Clusters::KubernetesNamespace.delete_all + end +end diff --git a/db/post_migrate/20181030135124_fill_empty_finished_at_in_deployments.rb b/db/post_migrate/20181030135124_fill_empty_finished_at_in_deployments.rb new file mode 100644 index 00000000000..32b271c472a --- /dev/null +++ b/db/post_migrate/20181030135124_fill_empty_finished_at_in_deployments.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class FillEmptyFinishedAtInDeployments < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + DEPLOYMENT_STATUS_SUCCESS = 2 # Equivalent to Deployment.statuses[:success] + + class Deployments < ActiveRecord::Base + self.table_name = 'deployments' + + include EachBatch + end + + def up + FillEmptyFinishedAtInDeployments::Deployments + .where('finished_at IS NULL') + .where('status = ?', DEPLOYMENT_STATUS_SUCCESS) + .each_batch(of: 10_000) do |relation| + relation.update_all('finished_at=created_at') + end + end + + def down + # no-op + end +end diff --git a/db/schema.rb b/db/schema.rb index ac1395d201a..b6b89c703fb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20181031190559) do +ActiveRecord::Schema.define(version: 20181101144347) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -165,6 +165,7 @@ ActiveRecord::Schema.define(version: 20181031190559) do t.integer "usage_stats_set_by_user_id" t.integer "receive_max_input_size" t.integer "diff_max_patch_bytes", default: 102400, null: false + t.integer "archive_builds_in_seconds" end create_table "audit_events", force: :cascade do |t| @@ -824,13 +825,18 @@ ActiveRecord::Schema.define(version: 20181031190559) do t.datetime "created_at" t.datetime "updated_at" t.string "on_stop" + t.integer "status", limit: 2, default: 2, null: false + t.datetime_with_timezone "finished_at" end add_index "deployments", ["created_at"], name: "index_deployments_on_created_at", using: :btree add_index "deployments", ["deployable_type", "deployable_id"], name: "index_deployments_on_deployable_type_and_deployable_id", using: :btree add_index "deployments", ["environment_id", "id"], name: "index_deployments_on_environment_id_and_id", using: :btree add_index "deployments", ["environment_id", "iid", "project_id"], name: "index_deployments_on_environment_id_and_iid_and_project_id", using: :btree + add_index "deployments", ["environment_id", "status"], name: "index_deployments_on_environment_id_and_status", using: :btree + add_index "deployments", ["id"], name: "partial_index_deployments_for_legacy_successful_deployments", where: "((finished_at IS NULL) AND (status = 2))", using: :btree add_index "deployments", ["project_id", "iid"], name: "index_deployments_on_project_id_and_iid", unique: true, using: :btree + add_index "deployments", ["project_id", "status"], name: "index_deployments_on_project_id_and_status", using: :btree create_table "emails", force: :cascade do |t| t.integer "user_id", null: false @@ -918,6 +924,35 @@ ActiveRecord::Schema.define(version: 20181031190559) do add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree + create_table "gcp_clusters", force: :cascade do |t| + t.integer "project_id", null: false + t.integer "user_id" + t.integer "service_id" + t.integer "status" + t.integer "gcp_cluster_size", null: false + t.datetime_with_timezone "created_at", null: false + t.datetime_with_timezone "updated_at", null: false + t.boolean "enabled", default: true + t.text "status_reason" + t.string "project_namespace" + t.string "endpoint" + t.text "ca_cert" + t.text "encrypted_kubernetes_token" + t.string "encrypted_kubernetes_token_iv" + t.string "username" + t.text "encrypted_password" + t.string "encrypted_password_iv" + t.string "gcp_project_id", null: false + t.string "gcp_cluster_zone", null: false + t.string "gcp_cluster_name", null: false + t.string "gcp_machine_type" + t.string "gcp_operation_id" + t.text "encrypted_gcp_token" + t.string "encrypted_gcp_token_iv" + end + + add_index "gcp_clusters", ["project_id"], name: "index_gcp_clusters_on_project_id", unique: true, using: :btree + create_table "gpg_key_subkeys", force: :cascade do |t| t.integer "gpg_key_id", null: false t.binary "keyid" @@ -1294,6 +1329,7 @@ ActiveRecord::Schema.define(version: 20181031190559) do add_index "merge_requests", ["created_at"], name: "index_merge_requests_on_created_at", using: :btree add_index "merge_requests", ["description"], name: "index_merge_requests_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} add_index "merge_requests", ["head_pipeline_id"], name: "index_merge_requests_on_head_pipeline_id", using: :btree + add_index "merge_requests", ["id", "merge_jid"], name: "index_merge_requests_on_id_and_merge_jid", where: "((merge_jid IS NOT NULL) AND ((state)::text = 'locked'::text))", using: :btree add_index "merge_requests", ["latest_merge_request_diff_id"], name: "index_merge_requests_on_latest_merge_request_diff_id", using: :btree add_index "merge_requests", ["merge_user_id"], name: "index_merge_requests_on_merge_user_id", where: "(merge_user_id IS NOT NULL)", using: :btree add_index "merge_requests", ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree @@ -1587,6 +1623,7 @@ ActiveRecord::Schema.define(version: 20181031190559) do t.datetime_with_timezone "created_at", null: false end + add_index "project_deploy_tokens", ["deploy_token_id"], name: "index_project_deploy_tokens_on_deploy_token_id", using: :btree add_index "project_deploy_tokens", ["project_id", "deploy_token_id"], name: "index_project_deploy_tokens_on_project_id_and_deploy_token_id", unique: true, using: :btree create_table "project_features", force: :cascade do |t| @@ -1702,6 +1739,7 @@ ActiveRecord::Schema.define(version: 20181031190559) do t.integer "jobs_cache_index" t.boolean "pages_https_only", default: true t.boolean "remote_mirror_available_overridden" + t.integer "pool_repository_id", limit: 8 end add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree @@ -1718,6 +1756,7 @@ ActiveRecord::Schema.define(version: 20181031190559) do add_index "projects", ["path"], name: "index_projects_on_path", using: :btree add_index "projects", ["path"], name: "index_projects_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"} add_index "projects", ["pending_delete"], name: "index_projects_on_pending_delete", using: :btree + add_index "projects", ["pool_repository_id"], name: "index_projects_on_pool_repository_id", where: "(pool_repository_id IS NOT NULL)", using: :btree add_index "projects", ["repository_storage", "created_at"], name: "idx_project_repository_check_partial", where: "(last_repository_check_at IS NULL)", using: :btree add_index "projects", ["repository_storage"], name: "index_projects_on_repository_storage", using: :btree add_index "projects", ["runners_token"], name: "index_projects_on_runners_token", using: :btree @@ -1850,6 +1889,14 @@ ActiveRecord::Schema.define(version: 20181031190559) do add_index "remote_mirrors", ["last_successful_update_at"], name: "index_remote_mirrors_on_last_successful_update_at", using: :btree add_index "remote_mirrors", ["project_id"], name: "index_remote_mirrors_on_project_id", using: :btree + create_table "repositories", id: :bigserial, force: :cascade do |t| + t.integer "shard_id", null: false + t.string "disk_path", null: false + end + + add_index "repositories", ["disk_path"], name: "index_repositories_on_disk_path", unique: true, using: :btree + add_index "repositories", ["shard_id"], name: "index_repositories_on_shard_id", using: :btree + create_table "repository_languages", id: false, force: :cascade do |t| t.integer "project_id", null: false t.integer "programming_language_id", null: false @@ -1930,6 +1977,12 @@ ActiveRecord::Schema.define(version: 20181031190559) do add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree add_index "services", ["template"], name: "index_services_on_template", using: :btree + create_table "shards", force: :cascade do |t| + t.string "name", null: false + end + + add_index "shards", ["name"], name: "index_shards_on_name", unique: true, using: :btree + create_table "site_statistics", force: :cascade do |t| t.integer "repositories_count", default: 0, null: false end @@ -2383,6 +2436,9 @@ ActiveRecord::Schema.define(version: 20181031190559) do add_foreign_key "fork_network_members", "projects", on_delete: :cascade add_foreign_key "fork_networks", "projects", column: "root_project_id", name: "fk_e7b436b2b5", on_delete: :nullify add_foreign_key "forked_project_links", "projects", column: "forked_to_project_id", name: "fk_434510edb0", on_delete: :cascade + add_foreign_key "gcp_clusters", "projects", on_delete: :cascade + add_foreign_key "gcp_clusters", "services", on_delete: :nullify + add_foreign_key "gcp_clusters", "users", on_delete: :nullify add_foreign_key "gpg_key_subkeys", "gpg_keys", on_delete: :cascade add_foreign_key "gpg_keys", "users", on_delete: :cascade add_foreign_key "gpg_signatures", "gpg_key_subkeys", on_delete: :nullify @@ -2449,6 +2505,7 @@ ActiveRecord::Schema.define(version: 20181031190559) do add_foreign_key "project_import_data", "projects", name: "fk_ffb9ee3a10", on_delete: :cascade add_foreign_key "project_mirror_data", "projects", on_delete: :cascade add_foreign_key "project_statistics", "projects", on_delete: :cascade + add_foreign_key "projects", "repositories", column: "pool_repository_id", name: "fk_6e5c14658a", on_delete: :nullify add_foreign_key "prometheus_metrics", "projects", on_delete: :cascade add_foreign_key "protected_branch_merge_access_levels", "protected_branches", name: "fk_8a3072ccb3", on_delete: :cascade add_foreign_key "protected_branch_push_access_levels", "protected_branches", name: "fk_9ffc86a3d9", on_delete: :cascade @@ -2460,6 +2517,7 @@ ActiveRecord::Schema.define(version: 20181031190559) do add_foreign_key "push_event_payloads", "events", name: "fk_36c74129da", on_delete: :cascade add_foreign_key "releases", "projects", name: "fk_47fe2a0596", on_delete: :cascade add_foreign_key "remote_mirrors", "projects", on_delete: :cascade + add_foreign_key "repositories", "shards", on_delete: :restrict add_foreign_key "repository_languages", "projects", on_delete: :cascade add_foreign_key "resource_label_events", "issues", on_delete: :cascade add_foreign_key "resource_label_events", "labels", on_delete: :nullify |