【Redmine】登録済みのユーザ文書を別のプロジェクトに移す
はじめに
Redmine-users-jaのメーリングリストに、以下のような質問がありました
RedMica 3.0.1.stableを利用しています。
とあるプロジェクト下のユーザー文書にあるファイルを、新規作成した別のプロジェクト下のユーザー文書へ移動したいです。
カテゴリごとの一括ダウンロード機能は見つけましたが、一括アップロードまたはプロジェクト移動する手段はありませんでしょうか…?
管理者ユーザも使用可能ですので、管理者ならできる、といった手段でも構いません。
それともやはり、新規作成したプロジェクトに改めて1つ1つファイルをアップロードするしかないでしょうか。
設定画面からの操作やAPIでは実現できないとおもいますが、DBを直接操作することで可能ですので紹介します。
なお、私の環境はテスト用に作ったもので他になにもデータが入っていない状態です。DBを書き換えますので、テスト環境などで試し、実際に実行する場合も念のためバックアップをとってから作業されることをオススメします。
前提条件
この記事では、DBはpostgreSQLを利用しています。
今回の記事での操作
プロジェクトAに登録されている文書をプロジェクトBに移動させる操作を行います。
操作前のRedmineの状態
準備(psqlでRedmineのDBにアクセス)
以下のようにpostgresユーザになり、psqlコマンドでredmineのDBにアクセスします
# su - postgres
$ psql redmine
psql (10.23)
"help" でヘルプを表示します。
redmine=#
DB定義の確認
documentsテーブルとattachmentsテーブルの定義を確認します
redmine=# \d documents
テーブル "public.documents"
列 | 型 | 照合順序 | Null 値を許容 | デフォルト
-------------+-----------------------------+----------+---------------+---------------------------------------
id | integer | | not null | nextval('documents_id_seq'::regclass)
project_id | integer | | not null | 0
category_id | integer | | not null | 0
title | character varying | | not null | ''::character varying
description | text | | |
created_on | timestamp without time zone | | |
インデックス:
"documents_pkey" PRIMARY KEY, btree (id)
"documents_project_id" btree (project_id)
"index_documents_on_category_id" btree (category_id)
"index_documents_on_created_on" btree (created_on)
redmine=# \d attachments
テーブル "public.attachments"
列 | 型 | 照合順序 | Null 値を許容 | デフォルト
----------------+-----------------------------+----------+---------------+-----------------------------------------
id | integer | | not null | nextval('attachments_id_seq'::regclass)
container_id | integer | | |
container_type | character varying(30) | | |
filename | character varying | | not null | ''::character varying
disk_filename | character varying | | not null | ''::character varying
filesize | bigint | | not null | 0
content_type | character varying | | | ''::character varying
digest | character varying(64) | | not null | ''::character varying
downloads | integer | | not null | 0
author_id | integer | | not null | 0
created_on | timestamp without time zone | | |
description | character varying | | |
disk_directory | character varying | | |
インデックス:
"attachments_pkey" PRIMARY KEY, btree (id)
"index_attachments_on_author_id" btree (author_id)
"index_attachments_on_container_id_and_container_type" btree (container_id, container_type)
"index_attachments_on_created_on" btree (created_on)
"index_attachments_on_disk_filename" btree (disk_filename)
これだけみてもよくわからないかもしれませんがテーブルの関連は以下のようになっています。
attachmentsテーブルに、実際の添付ファイルにアクセスするための情報(ファイル名など)が格納されており、documentsテーブルには文書タブで表示するための情報が格納されています。documentsテーブルのIDが、attachmentsテーブルのコンテナIDになっています。また、ここでは説明は割愛しますが、projectsテーブルにプロジェクトの情報が入っています。
DBのデータの確認
定義のほうをみてもわかりにくいかもしれませんが、DBのデータをみると一目瞭然です。documentsテーブルのところにproject_idのデータが入っていて、添付文書はいずれもAプロジェクトのものであることがわかります。
redmine=# select id,name from projects;
id | name
----+----------------
1 | Aプロジェクト
2 | Bプロジェクト
(2 行)
redmine=# select id,project_id,title from documents;
id | project_id | title
----+------------+--------------------
3 | 1 | テスト用添付文書2
2 | 1 | テスト用添付文書
(2 行)
redmine=# select id,container_id,filename from attachments;
id | container_id | filename
----+--------------+------------------------
2 | 2 | テスト用添付文書.txt
3 | 3 | テスト用添付文書2.txt
DBのデータの書換え
documentsに格納されているプロジェクトの情報をAプロジェクト→Bプロジェクトに書き換えます。ここではDBを書き換えますので、細心の注意の上実施してください。Postgresqlではデフォルトで自動コミットが有効ですので、updateコマンドを実行するとすぐにDBが更新されます。
redmine=# update documents set project_id=2 where project_id=1;
UPDATE 2
redmine=# select id,project_id,title from documents;
id | project_id | title
----+------------+--------------------
2 | 2 | テスト用添付文書
3 | 2 | テスト用添付文書2
(2 行)
操作後のRedmineの状態確認
以下のように、Aプロジェクトの文書がBプロジェクトに移動できました
別解(Rails consoleを使う方法)
詳細な説明は割愛しますが、Railsコンソールを使う方法もあります
準備(Railsコンソール起動)
redmineのインストールディレクトリでRailsコンソールを起動します
# cd /var/lib/redmine
# bin/rails console -e production
Loading production environment (Rails 7.1.2)
irb(main):006>
データの確認
irb(main):004> Document.where(project_id: 1)
=>
[#<Document:0x00007fa0e6169c50
id: 2,
project_id: 1,
category_id: 6,
title: "テスト用添付文書",
description: "テスト用添付文書",
created_on: Sat, 31 Aug 2024 16:32:21.853935000 UTC +00:00>,
#<Document:0x00007fa0e61699d0
id: 3,
project_id: 1,
category_id: 6,
title: "テスト用添付文書2",
description: "テスト用添付文書2",
created_on: Sat, 31 Aug 2024 16:33:52.633060000 UTC +00:00>]
データの更新(一括更新)
irb(main):005> Document.where(project_id: 1).update_all(project_id: 2)
=> 2
irb(main):006> Document.where(project_id: 2)
=>
[#<Document:0x00007fa0e1fffc88
id: 2,
project_id: 2,
category_id: 6,
title: "テスト用添付文書",
description: "テスト用添付文書",
created_on: Sat, 31 Aug 2024 16:32:21.853935000 UTC +00:00>,
#<Document:0x00007fa0e1fffb48
id: 3,
project_id: 2,
category_id: 6,
title: "テスト用添付文書2",
description: "テスト用添付文書2",
created_on: Sat, 31 Aug 2024 16:33:52.633060000 UTC +00:00>]
これでproject_idが1のドキュメントがproject_id=2に変更されました。
データの更新(レコード毎更新)
最初のDBを直接書き換える方法、別解のRails consoleを使う方法(一括更新)の場合、Railsのバリデーションは行われません。以下のようにすればレコード毎に更新されますのでバリデーションも有効になります。
irb(main):005> Document.where(project_id: 1).find_each do |document|
document.update(project_id: 2)
end
最後に
書いてから思いましたが、実運用においてはRailsコンソールを使うのが適切かもしれませんね。
postgresqlに直接アクセスする方法は、テーブル構造や中身のデータを確認しやすい(Railsコンソールでもできることはできますが、Modelの情報を知らないと操作しにくい)ので、他の操作への応用もしやすいとおもいます。
いずれか、お好みの方法で実施していただければよいかとおもいます。
参考になれば幸いです。
Discussion