[Django]複合主キー

0.Djangoは複合主キーをサポートしている?

https://docs.djangoproject.com/ja/3.1/faq/models/#do-django-models-support-multiple-column-primary-keys

https://code.djangoproject.com/wiki/MultipleColumnPrimaryKeys

Djangoでは複合主キーのサポートは公式にはされていない。※対応するパッケージとして以下を提供済み

「primary_key=True」が指定されているフィールドが無い場合は、自動で「id」が主キーとして追加されて動作するようなので、従来システムのDBを使っている場合、モデルを使った更新、削除が、このままでは動作しない。テーブルにidフィールドを追加するか、独自のクエリで処理する必要がある。

1.解決方法

案1:サロゲートキーを追加する

サロゲートキー(“id”)をDBに追加して、複合キーには一意制約(unique_together)を設定すれば、Djangoのモデルがそのまま使える。

(問題)DBの再構築に時間がかかり、計画停止が必要。

案2:独自クエリ―にて実装する

ORMに頼らずに、自分でクエリ―を書く実装にする。クエリの性能面等を自分でコントロールできる。

(問題)コードとテストが増える。

案3:Modelを拡張する

Djangoプロジェクトでも以前に検討されたようだが、最近はどうなっているかは、よく分からない。

https://code.djangoproject.com/ticket/373

https://code.djangoproject.com/wiki/MultipleColumnPrimaryKeys#CurrentStatus

色々、挑戦した人もいるみたいだが、結局、どれも未完か?

とりあえず、自分でモデルを拡張することで、自分の仕事で使えるように、Modelのサブクラスを作成する。とりあえずは、Djangoのすべての機能をカバーする必要はないので、あくまでも自分が必要とする単体モデルのINSERT,UPDATE,DELETEがカバーできるものならできそう。

(問題)現状のModelのコードを理解する必要がある。

案4:別のORMを作る

モデルまわりのコードを見たが、少しスパゲッティな感じもした。ざっと実装を調べていて感じたのは、Model、Manager、QuerySetでの役割がきれいに分かれてないように見受けられた。複合キー対応も含めて、簡素な自分のORM作るほうがいいような気もする。

(問題)そんな時間は無い!

案5:既にある別のORMを使う

以下は、主キーではなくて、あくまで複合外部キーのためか?

https://github.com/Arisophy/django-composite-foreignkey

(問題)適切なものが見つからない

2.結論

今のプロジェクトは、Ruby On Railsで作られたレガシーなシステムを、徐々にDjangoに移行しているところで、DBに手を加えたくない。

結論として、案2と案3で進めることにした。

INSERT,UPDATE,DELETEについては、案3でModelの拡張クラスで対応する。実際に作ってみたら、少ないコードで、いい感じで動いている。このままその拡張モデルを使いながら、プロジェクトを進めていく。

SELECTについては、そもそも、すべてORMに頼るわけにはいかないので、独自クエリ―も利用する。

拡張クラスをどう作ったかは、後日、公開します。

(2021/01/29)

以下で、パッケージを公開しました。

https://github.com/Arisophy/django-compositepk-model

(2021/06/06)

パッケージの日本語説明

(2021/03/16)

Djangoのticket373についての自分の見解は以下。

[Django]DB操作

1.モデル

https://docs.djangoproject.com/ja/3.2/topics/db/models/

1.1 オブジェクトの取得

オブジェクトを取得する

1)全件取得

MyModel.objects.all()

2)1件の取得

get() を用いて1つのオブジェクトを取得する

3)複雑な条件

以下で様々な条件が作れる。

Q オブジェクトを用いた複雑な検索

Field lookups

集計して、さらにその条件で検索するような場合は、以下を参考。

アグリゲーション

Filtering on annotations

1.2 リレーション

リレーション

関係により、以下クラスでFieldを定義する。リレーションを定義された側のモデルでは、自動で小文字のモデル名でフィールドが作成されて利用できるようになる。

反対向きのリレーション

1)多対一 (many-to-one) 関係

class ForeignKey(to, on_delete, **options)

必須ではありませんが、ForeignKey フィールド名 (上記の例では manufacturer はモデル名を小文字にしたものをおすすめします。

Djangoドキュメント:モデル-リレーション

2)多対多 (many-to-many) 関係

class ManyToManyField(to, **options)

3)一対一 (one-to-one) 関係

class OneToOneField(to, on_delete, parent_link=False, **options)

オブジェクトのhasattrは、以下でやる。

hasattr(object, 'field_name')

https://docs.djangoproject.com/ja/3.1/topics/db/examples/one_to_one/#one-to-one-relationships

4)実装例

Examples of model relationship API

5)その他

・キーは、無指定の場合、モデル名+’_id’となる。違う定義の場合は、db_columnで指定する。

Database Representation

・逆側のリレーションを作りたくない場合は、「related_name=’+’」

https://docs.djangoproject.com/ja/3.1/ref/models/fields/#django.db.models.ForeignKey.related_name

2.独自のクエリ

1)カスタムSQLの操作

https://docs.djangoproject.com/en/3.1/topics/db/sql/#executing-custom-sql-directly

2)トランザクション制御

https://docs.djangoproject.com/ja/3.1/topics/db/transactions/

明示的にトランザクションをコントロールする

3)素の SQL 文の実行

https://docs.djangoproject.com/ja/3.1/topics/db/sql/#performing-raw-sql-queries

3.複合主キーの扱い

https://docs.djangoproject.com/ja/3.1/faq/models/#do-django-models-support-multiple-column-primary-keys

https://code.djangoproject.com/wiki/MultipleColumnPrimaryKeys

複合主キーはサポートされていなくて、「primary_key=True」指定されているフィールドが無い場合は、「id」が主キーとして動作するので、従来システムのDBを使っている場合、モデルを使った更新、削除が、このままでは動作しない。テーブルにidフィールドを追加するか、独自のクエリで処理する必要がある。

3.Manager

3.1 カスタムマネージャーの作成

DB取得等のロジックをViewの中に色々書くのもいまいちなので、カスタムマネージャーを作成する。

(Django公式)マネージャーのカスタマイズ

上記の日本語版は、翻訳が微妙で間違った解釈になるところがあるので、英語版を読んだ方が良さそう。

(Django公式)Custom managers

拡張クラスにメソッドを定義、あるいはget_querysetを書き換える等をコーディングするなり、QuerySetを定義するなりする。以下に4パターンの方法が記載されている。

(Django公式ドキュメント)

①カスタムMangerにメソッド追加する方法

Adding extra manager methods

②カスタムMangerの初期クエリセットを変更する方法

Modifying a manager’s initial QuerySet

③QuerySetを拡張してメソッド追加する方法

Calling custom QuerySet methods from the manager

④QuerySetを複数拡張する場合に、Managerをクエリセットから定義できる方法

Creating a manager with QuerySet methods

上記から適切な方法で実装していく。

4.QuerySet

https://docs.djangoproject.com/ja/3.1/ref/models/querysets/

WordPressのXserverへの移行

お名前.comのレンタルサーバーから、Xserverのレンタルサーバーへ移行した。WordPressの移行については、「WordPress簡単移行」で移行した。

https://www.xserver.ne.jp/manual/man_install_transfer_wp.php

便利で楽にできていいのだが、ただ、一つのサイトだけデータ移行のところで失敗して移行できなかった。

【原因】

プラグインの「Google Map WP」がDBでバイナリデータを使っていて、それが通常方法でdumpした場合には復元できないためエラーとなるよう。

【対応方法】

「WordPress簡単移行」を使いたい場合は、一旦、「Google Map WP」プラグインを削除してから移行して、その後、Xser ver側で再度、プラグインを入れるしかないか。

そうじゃなければ、自分で移行する。

https://www.xserver.ne.jp/manual/man_install_transfer_wordpress.php

その場合、DBバックアップ時には「–hex-blob」オプションを指定すること。

https://dev.mysql.com/doc/refman/5.6/ja/mysqldump.html#option_mysqldump_hex-blob

OSSへの参加

Djangoで開発している中で、汎用的なクラスを公開したほうがいいのかなと思い、今までは躊躇していたが、OSSに参加することにした。利用させてもらうだけというのも、確かに申し訳ないし。

まずは、GitHubで業務に関係のない共通的なクラスで、他の人にも役立ちそうなものを登録して、Djangoコミュニティーにも参加していこうかと思う。

1.GitHub

仕事関係は、自分でGitサーバーを立てて管理していたが、共通ライブラリーはGitHubに入れるようにする。

1.1 オープンソースについて

まずは、オープンソースについて知識を入れる。

オープンソースガイドライン

1.2 マークダウン言語

https://guides.github.com/features/mastering-markdown/

https://gist.github.com/mignonstyle/083c9e1651d7734f84c99b8cf49d57fa

1.3 レポジトリーを公開

最初の公開レポジトリーを作成した。

https://github.com/Arisophy/django-searchview

2.Django

(オープンソースとしてのDjango)

https://docs.djangoproject.com/ja/3.1/#the-django-open-source-project

(DjangoのGitHub)

https://github.com/django/django

2.1 Djangoメーリングリストへの参加

まずは、開発者メーリングリストに登録した。

https://docs.djangoproject.com/ja/3.1/internals/mailing-lists/#django-developers

3.パッケージを作る

パッケージを作って、以下に登録した。

https://pypi.org/

最初のパッケージは、以下を作った。

https://pypi.org/project/django-searchview-lib/

3.1 パッケージ作成手順

https://packaging.python.org/tutorials/packaging-projects/

1)以下コマンドでパッケージ作る

python setup.py sdist bdist_wheel

2)パッケージをアップロード

(テスト環境)
python -m twine upload --repository testpypi dist/*

(リリース)
python -m twine upload dist/*