mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-10-08 12:56:27 +08:00
Merge pull request #7694 from rekonder/aj_SCI_10465
Add local storage backend [SCI-10465]
This commit is contained in:
commit
97d2513a04
14 changed files with 377 additions and 4 deletions
|
@ -0,0 +1,89 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class StorageLocationRepositoryRowsController < ApplicationController
|
||||||
|
before_action :load_storage_location_repository_row, only: %i(update destroy)
|
||||||
|
before_action :load_storage_location
|
||||||
|
before_action :load_repository_row
|
||||||
|
before_action :check_read_permissions, only: :index
|
||||||
|
before_action :check_manage_permissions, except: :index
|
||||||
|
|
||||||
|
def index
|
||||||
|
storage_location_repository_row = Lists::StorageLocationRepositoryRowsService.new(
|
||||||
|
current_team, storage_location_repository_row_params
|
||||||
|
).call
|
||||||
|
render json: storage_location_repository_row,
|
||||||
|
each_serializer: Lists::StorageLocationRepositoryRowSerializer,
|
||||||
|
include: %i(repository_row)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@storage_location_repository_row.update(storage_location_repository_row_params)
|
||||||
|
|
||||||
|
if @storage_location_repository_row.save
|
||||||
|
render json: @storage_location_repository_row,
|
||||||
|
serializer: Lists::StorageLocationRepositoryRowSerializer,
|
||||||
|
include: :repository_row
|
||||||
|
else
|
||||||
|
render json: @storage_location_repository_row.errors, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@storage_location_repository_row = StorageLocationRepositoryRow.new(
|
||||||
|
repository_row: @repository_row,
|
||||||
|
storage_location: @storage_location,
|
||||||
|
metadata: storage_location_repository_row_params[:metadata] || {},
|
||||||
|
created_by: current_user
|
||||||
|
)
|
||||||
|
|
||||||
|
if @storage_location_repository_row.save
|
||||||
|
render json: @storage_location_repository_row,
|
||||||
|
serializer: Lists::StorageLocationRepositoryRowSerializer,
|
||||||
|
include: :repository_row
|
||||||
|
else
|
||||||
|
render json: @storage_location_repository_row.errors, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
if @storage_location_repository_row.discard
|
||||||
|
render json: {}
|
||||||
|
else
|
||||||
|
render json: { errors: @storage_location_repository_row.errors.full_messages }, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_storage_location_repository_row
|
||||||
|
@storage_location_repository_row = StorageLocationRepositoryRow.find(
|
||||||
|
storage_location_repository_row_params[:id]
|
||||||
|
)
|
||||||
|
render_404 unless @storage_location_repository_row
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_storage_location
|
||||||
|
@storage_location = StorageLocation.where(team: current_team).find(
|
||||||
|
storage_location_repository_row_params[:storage_location_id]
|
||||||
|
)
|
||||||
|
render_404 unless @storage_location
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_repository_row
|
||||||
|
@repository_row = RepositoryRow.find(storage_location_repository_row_params[:repository_row_id])
|
||||||
|
render_404 unless @repository_row
|
||||||
|
end
|
||||||
|
|
||||||
|
def storage_location_repository_row_params
|
||||||
|
params.permit(:id, :storage_location_id, :repository_row_id,
|
||||||
|
metadata: { position: [] })
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_read_permissions
|
||||||
|
render_403 unless true
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_manage_permissions
|
||||||
|
render_403 unless true
|
||||||
|
end
|
||||||
|
end
|
65
app/controllers/storage_locations_controller.rb
Normal file
65
app/controllers/storage_locations_controller.rb
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class StorageLocationsController < ApplicationController
|
||||||
|
before_action :load_storage_location, only: %i(update destroy)
|
||||||
|
before_action :check_read_permissions, only: :index
|
||||||
|
before_action :check_manage_permissions, except: :index
|
||||||
|
|
||||||
|
def index
|
||||||
|
storage_locations = Lists::StorageLocationsService.new(current_team, storage_location_params).call
|
||||||
|
render json: storage_locations, each_serializer: Lists::StorageLocationSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@storage_location.image.attach(storage_location_params[:signed_blob_id]) if storage_location_params[:signed_blob_id]
|
||||||
|
@storage_location.update(storage_location_params)
|
||||||
|
|
||||||
|
if @storage_location.save
|
||||||
|
render json: @storage_location, serializer: Lists::StorageLocationSerializer
|
||||||
|
else
|
||||||
|
render json: @storage_location.errors, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@storage_location = StorageLocation.new(
|
||||||
|
storage_location_params.merge({ team: current_team, created_by: current_user })
|
||||||
|
)
|
||||||
|
|
||||||
|
@storage_location.image.attach(storage_location_params[:signed_blob_id]) if storage_location_params[:signed_blob_id]
|
||||||
|
|
||||||
|
if @storage_location.save
|
||||||
|
render json: @storage_location, serializer: Lists::StorageLocationSerializer
|
||||||
|
else
|
||||||
|
render json: @storage_location.errors, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
if @storage_location.discard
|
||||||
|
render json: {}
|
||||||
|
else
|
||||||
|
render json: { errors: @storage_location.errors.full_messages }, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def storage_location_params
|
||||||
|
params.permit(:id, :parent_id, :name, :container, :signed_blob_id, :description,
|
||||||
|
metadata: { dimensions: [], parent_coordinations: [], display_type: :string })
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_storage_location
|
||||||
|
@storage_location = StorageLocation.where(team: current_team).find(storage_location_params[:id])
|
||||||
|
render_404 unless @storage_location
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_read_permissions
|
||||||
|
render_403 unless true
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_manage_permissions
|
||||||
|
render_403 unless true
|
||||||
|
end
|
||||||
|
end
|
|
@ -98,6 +98,8 @@ class RepositoryRow < ApplicationRecord
|
||||||
class_name: 'RepositoryRow',
|
class_name: 'RepositoryRow',
|
||||||
source: :parent,
|
source: :parent,
|
||||||
dependent: :destroy
|
dependent: :destroy
|
||||||
|
has_many :storage_location_repository_rows, inverse_of: :repository_row, dependent: :destroy
|
||||||
|
has_many :storage_locations, through: :storage_location_repository_rows
|
||||||
|
|
||||||
auto_strip_attributes :name, nullify: false
|
auto_strip_attributes :name, nullify: false
|
||||||
validates :name,
|
validates :name,
|
||||||
|
|
25
app/models/storage_location.rb
Normal file
25
app/models/storage_location.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class StorageLocation < ApplicationRecord
|
||||||
|
include Discard::Model
|
||||||
|
ID_PREFIX = 'SL'
|
||||||
|
include PrefixedIdModel
|
||||||
|
|
||||||
|
default_scope -> { kept }
|
||||||
|
|
||||||
|
has_one_attached :image
|
||||||
|
|
||||||
|
belongs_to :team
|
||||||
|
belongs_to :parent, class_name: 'StorageLocation', optional: true
|
||||||
|
belongs_to :created_by, class_name: 'User'
|
||||||
|
|
||||||
|
has_many :storage_location_repository_rows, inverse_of: :storage_location
|
||||||
|
has_many :repository_rows, through: :storage_location_repository_row
|
||||||
|
|
||||||
|
validates :name, length: { maximum: Constants::NAME_MAX_LENGTH }
|
||||||
|
|
||||||
|
after_discard do
|
||||||
|
StorageLocation.where(parent_id: id).find_each(&:discard)
|
||||||
|
storage_location_repository_rows.each(&:discard)
|
||||||
|
end
|
||||||
|
end
|
30
app/models/storage_location_repository_row.rb
Normal file
30
app/models/storage_location_repository_row.rb
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class StorageLocationRepositoryRow < ApplicationRecord
|
||||||
|
include Discard::Model
|
||||||
|
|
||||||
|
default_scope -> { kept }
|
||||||
|
|
||||||
|
belongs_to :storage_location, inverse_of: :storage_location_repository_rows
|
||||||
|
belongs_to :repository_row, inverse_of: :storage_location_repository_rows
|
||||||
|
belongs_to :created_by, class_name: 'User'
|
||||||
|
|
||||||
|
with_options if: -> { storage_location.container && storage_location.metadata['type'] == 'grid' } do
|
||||||
|
validate :position_must_be_present
|
||||||
|
validate :ensure_uniq_position
|
||||||
|
end
|
||||||
|
|
||||||
|
def position_must_be_present
|
||||||
|
if metadata['position'].blank?
|
||||||
|
errors.add(:base, I18n.t('activerecord.errors.models.storage_location.missing_position'))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_uniq_position
|
||||||
|
if StorageLocationRepositoryRow.where(storage_location: storage_location)
|
||||||
|
.where('metadata @> ?', { position: metadata['position'] }.to_json)
|
||||||
|
.where.not(id: id).exists?
|
||||||
|
errors.add(:base, I18n.t('activerecord.errors.models.storage_location.not_uniq_position'))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Lists
|
||||||
|
class StorageLocationRepositoryRowSerializer < ActiveModel::Serializer
|
||||||
|
attributes :created_by, :created_on, :position
|
||||||
|
|
||||||
|
belongs_to :repository_row, serializer: RepositoryRowSerializer
|
||||||
|
|
||||||
|
def created_by
|
||||||
|
object.created_by.full_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def created_on
|
||||||
|
I18n.l(object.created_at, format: :full)
|
||||||
|
end
|
||||||
|
|
||||||
|
def position
|
||||||
|
object.metadata['position']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
19
app/serializers/lists/storage_location_serializer.rb
Normal file
19
app/serializers/lists/storage_location_serializer.rb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Lists
|
||||||
|
class StorageLocationSerializer < ActiveModel::Serializer
|
||||||
|
attributes :id, :code, :name, :container, :description, :owned_by, :created_by, :created_on
|
||||||
|
|
||||||
|
def owned_by
|
||||||
|
object.team.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def created_by
|
||||||
|
object.created_by.full_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def created_on
|
||||||
|
I18n.l(object.created_at, format: :full)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -29,7 +29,7 @@ module Lists
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginate_records
|
def paginate_records
|
||||||
@records = @records.page(@params[:page]).per(@params[:per_page])
|
@records = @records.page(@params[:page]).per(@params[:per_page]) if @params[:page].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def sort_direction(order_params)
|
def sort_direction(order_params)
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Lists
|
||||||
|
class StorageLocationRepositoryRowsService < BaseService
|
||||||
|
def initialize(team, params)
|
||||||
|
@team = team
|
||||||
|
@storage_location_id = params[:storage_location_id]
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_records
|
||||||
|
@records = StorageLocationRepositoryRow.includes(:repository_row).where(storage_location_id: @storage_location_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_records; end
|
||||||
|
end
|
||||||
|
end
|
17
app/services/lists/storage_locations_service.rb
Normal file
17
app/services/lists/storage_locations_service.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Lists
|
||||||
|
class StorageLocationsService < BaseService
|
||||||
|
def initialize(team, params)
|
||||||
|
@team = team
|
||||||
|
@parent_id = params[:parent_id]
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_records
|
||||||
|
@records = StorageLocation.where(team: @team, parent_id: @parent_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_records; end
|
||||||
|
end
|
||||||
|
end
|
|
@ -259,6 +259,9 @@ en:
|
||||||
attributes:
|
attributes:
|
||||||
text: Text is too long
|
text: Text is too long
|
||||||
position: "Position has already been taken by another item in the checklist"
|
position: "Position has already been taken by another item in the checklist"
|
||||||
|
storage_location:
|
||||||
|
missing_position: 'Missing position metadata'
|
||||||
|
not_uniq_position: 'Position already taken'
|
||||||
storage:
|
storage:
|
||||||
limit_reached: "Storage limit has been reached."
|
limit_reached: "Storage limit has been reached."
|
||||||
helpers:
|
helpers:
|
||||||
|
|
|
@ -807,6 +807,10 @@ Rails.application.routes.draw do
|
||||||
|
|
||||||
resources :connected_devices, controller: 'users/connected_devices', only: %i(destroy)
|
resources :connected_devices, controller: 'users/connected_devices', only: %i(destroy)
|
||||||
|
|
||||||
|
resources :storage_locations, only: %i(index create destroy update) do
|
||||||
|
resources :storage_location_repository_rows, only: %i(index create destroy update)
|
||||||
|
end
|
||||||
|
|
||||||
get 'search' => 'search#index'
|
get 'search' => 'search#index'
|
||||||
get 'search/new' => 'search#new', as: :new_search
|
get 'search/new' => 'search#new', as: :new_search
|
||||||
resource :search, only: [], controller: :search do
|
resource :search, only: [], controller: :search do
|
||||||
|
|
38
db/migrate/20240705122903_add_storage_locations.rb
Normal file
38
db/migrate/20240705122903_add_storage_locations.rb
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddStorageLocations < ActiveRecord::Migration[7.0]
|
||||||
|
include DatabaseHelper
|
||||||
|
|
||||||
|
def up
|
||||||
|
create_table :storage_locations do |t|
|
||||||
|
t.string :name
|
||||||
|
t.string :description
|
||||||
|
t.references :parent, index: true, foreign_key: { to_table: :storage_locations }
|
||||||
|
t.references :team, index: true, foreign_key: { to_table: :teams }
|
||||||
|
t.references :created_by, foreign_key: { to_table: :users }
|
||||||
|
t.boolean :container, default: false, null: false, index: true
|
||||||
|
t.jsonb :metadata, null: false, default: {}
|
||||||
|
t.datetime :discarded_at, index: true
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table :storage_location_repository_rows do |t|
|
||||||
|
t.references :repository_row, index: true, foreign_key: { to_table: :repository_rows }
|
||||||
|
t.references :storage_location, index: true, foreign_key: { to_table: :storage_locations }
|
||||||
|
t.references :created_by, foreign_key: { to_table: :users }
|
||||||
|
t.jsonb :metadata, null: false, default: {}
|
||||||
|
t.datetime :discarded_at, index: true
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
add_gin_index_without_tags :storage_locations, :name
|
||||||
|
add_gin_index_without_tags :storage_locations, :description
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
drop_table :storage_location_repository_rows
|
||||||
|
drop_table :storage_locations
|
||||||
|
end
|
||||||
|
end
|
49
db/schema.rb
49
db/schema.rb
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.0].define(version: 2024_06_26_113515) do
|
ActiveRecord::Schema[7.0].define(version: 2024_07_05_122903) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "btree_gist"
|
enable_extension "btree_gist"
|
||||||
enable_extension "pg_trgm"
|
enable_extension "pg_trgm"
|
||||||
|
@ -721,8 +721,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_06_26_113515) do
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.string "type"
|
t.string "type"
|
||||||
t.datetime "start_time_dup"
|
t.datetime "start_time_dup", precision: nil
|
||||||
t.datetime "end_time_dup"
|
t.datetime "end_time_dup", precision: nil
|
||||||
t.index "((end_time)::date)", name: "index_repository_date_time_range_values_on_end_time_as_date", where: "((type)::text = 'RepositoryDateRangeValue'::text)"
|
t.index "((end_time)::date)", name: "index_repository_date_time_range_values_on_end_time_as_date", where: "((type)::text = 'RepositoryDateRangeValue'::text)"
|
||||||
t.index "((end_time)::time without time zone)", name: "index_repository_date_time_range_values_on_end_time_as_time", where: "((type)::text = 'RepositoryTimeRangeValue'::text)"
|
t.index "((end_time)::time without time zone)", name: "index_repository_date_time_range_values_on_end_time_as_time", where: "((type)::text = 'RepositoryTimeRangeValue'::text)"
|
||||||
t.index "((start_time)::date)", name: "index_repository_date_time_range_values_on_start_time_as_date", where: "((type)::text = 'RepositoryDateRangeValue'::text)"
|
t.index "((start_time)::date)", name: "index_repository_date_time_range_values_on_start_time_as_date", where: "((type)::text = 'RepositoryDateRangeValue'::text)"
|
||||||
|
@ -1083,6 +1083,40 @@ ActiveRecord::Schema[7.0].define(version: 2024_06_26_113515) do
|
||||||
t.index ["user_id"], name: "index_steps_on_user_id"
|
t.index ["user_id"], name: "index_steps_on_user_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "storage_location_repository_rows", force: :cascade do |t|
|
||||||
|
t.bigint "repository_row_id"
|
||||||
|
t.bigint "storage_location_id"
|
||||||
|
t.bigint "created_by_id"
|
||||||
|
t.jsonb "metadata", default: {}, null: false
|
||||||
|
t.datetime "discarded_at"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["created_by_id"], name: "index_storage_location_repository_rows_on_created_by_id"
|
||||||
|
t.index ["discarded_at"], name: "index_storage_location_repository_rows_on_discarded_at"
|
||||||
|
t.index ["repository_row_id"], name: "index_storage_location_repository_rows_on_repository_row_id"
|
||||||
|
t.index ["storage_location_id"], name: "index_storage_location_repository_rows_on_storage_location_id"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "storage_locations", force: :cascade do |t|
|
||||||
|
t.string "name"
|
||||||
|
t.string "description"
|
||||||
|
t.bigint "parent_id"
|
||||||
|
t.bigint "team_id"
|
||||||
|
t.bigint "created_by_id"
|
||||||
|
t.boolean "container", default: false, null: false
|
||||||
|
t.jsonb "metadata", default: {}, null: false
|
||||||
|
t.datetime "discarded_at"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index "trim_html_tags((description)::text) gin_trgm_ops", name: "index_storage_locations_on_description", using: :gin
|
||||||
|
t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_storage_locations_on_name", using: :gin
|
||||||
|
t.index ["container"], name: "index_storage_locations_on_container"
|
||||||
|
t.index ["created_by_id"], name: "index_storage_locations_on_created_by_id"
|
||||||
|
t.index ["discarded_at"], name: "index_storage_locations_on_discarded_at"
|
||||||
|
t.index ["parent_id"], name: "index_storage_locations_on_parent_id"
|
||||||
|
t.index ["team_id"], name: "index_storage_locations_on_team_id"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "tables", force: :cascade do |t|
|
create_table "tables", force: :cascade do |t|
|
||||||
t.binary "contents", null: false
|
t.binary "contents", null: false
|
||||||
t.datetime "created_at", precision: nil, null: false
|
t.datetime "created_at", precision: nil, null: false
|
||||||
|
@ -1278,6 +1312,9 @@ ActiveRecord::Schema[7.0].define(version: 2024_06_26_113515) do
|
||||||
t.integer "failed_attempts", default: 0, null: false
|
t.integer "failed_attempts", default: 0, null: false
|
||||||
t.datetime "locked_at", precision: nil
|
t.datetime "locked_at", precision: nil
|
||||||
t.string "unlock_token"
|
t.string "unlock_token"
|
||||||
|
t.string "api_key"
|
||||||
|
t.datetime "api_key_expires_at", precision: nil
|
||||||
|
t.datetime "api_key_created_at", precision: nil
|
||||||
t.index "trim_html_tags((full_name)::text) gin_trgm_ops", name: "index_users_on_full_name", using: :gin
|
t.index "trim_html_tags((full_name)::text) gin_trgm_ops", name: "index_users_on_full_name", using: :gin
|
||||||
t.index ["authentication_token"], name: "index_users_on_authentication_token", unique: true
|
t.index ["authentication_token"], name: "index_users_on_authentication_token", unique: true
|
||||||
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
|
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
|
||||||
|
@ -1502,6 +1539,12 @@ ActiveRecord::Schema[7.0].define(version: 2024_06_26_113515) do
|
||||||
add_foreign_key "steps", "protocols"
|
add_foreign_key "steps", "protocols"
|
||||||
add_foreign_key "steps", "users"
|
add_foreign_key "steps", "users"
|
||||||
add_foreign_key "steps", "users", column: "last_modified_by_id"
|
add_foreign_key "steps", "users", column: "last_modified_by_id"
|
||||||
|
add_foreign_key "storage_location_repository_rows", "repository_rows"
|
||||||
|
add_foreign_key "storage_location_repository_rows", "storage_locations"
|
||||||
|
add_foreign_key "storage_location_repository_rows", "users", column: "created_by_id"
|
||||||
|
add_foreign_key "storage_locations", "storage_locations", column: "parent_id"
|
||||||
|
add_foreign_key "storage_locations", "teams"
|
||||||
|
add_foreign_key "storage_locations", "users", column: "created_by_id"
|
||||||
add_foreign_key "tables", "users", column: "created_by_id"
|
add_foreign_key "tables", "users", column: "created_by_id"
|
||||||
add_foreign_key "tables", "users", column: "last_modified_by_id"
|
add_foreign_key "tables", "users", column: "last_modified_by_id"
|
||||||
add_foreign_key "tags", "projects"
|
add_foreign_key "tags", "projects"
|
||||||
|
|
Loading…
Add table
Reference in a new issue