👌

【Redmine】登録済みのユーザ文書を別のプロジェクトに移す

2024/09/01に公開

はじめに

Redmine-users-jaのメーリングリストに、以下のような質問がありました

RedMica 3.0.1.stableを利用しています。
とあるプロジェクト下のユーザー文書にあるファイルを、新規作成した別のプロジェクト下のユーザー文書へ移動したいです。
カテゴリごとの一括ダウンロード機能は見つけましたが、一括アップロードまたはプロジェクト移動する手段はありませんでしょうか…?
管理者ユーザも使用可能ですので、管理者ならできる、といった手段でも構いません。
それともやはり、新規作成したプロジェクトに改めて1つ1つファイルをアップロードするしかないでしょうか。

設定画面からの操作やAPIでは実現できないとおもいますが、DBを直接操作することで可能ですので紹介します。

なお、私の環境はテスト用に作ったもので他になにもデータが入っていない状態です。DBを書き換えますので、テスト環境などで試し、実際に実行する場合も念のためバックアップをとってから作業されることをオススメします。

前提条件

この記事では、DBはpostgreSQLを利用しています。

今回の記事での操作

プロジェクトAに登録されている文書をプロジェクトBに移動させる操作を行います。

操作前のRedmineの状態

操作前(Aプロジェクト)
操作前(Bプロジェクト)

準備(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プロジェクトに移動できました

操作前(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の情報を知らないと操作しにくい)ので、他の操作への応用もしやすいとおもいます。

いずれか、お好みの方法で実施していただければよいかとおもいます。

参考になれば幸いです。

GitHubで編集を提案

Discussion