Django ORM でのデータベース ビューの使用

データベース テーブルではなくデータベース ビューを使用すると、集計データのクエリに非常に役立つ場合があります。 通常、Django ORM では、データベース テーブルが Django ORM モデルに関連付けられますが、データベース ビューを Django ORM モデルに関連付けることも可能であることがわかりました。 そうすることで、データベース レベル (SQL 作成ビュー) でデータ集計ロジックを非表示にすることができます。 また、二重アンダースコア外部キー検索などの ORM 機能のほとんどは、データベース ビューに関連付けられたモデルでも引き続き機能します。 このブログ投稿では、その方法を説明する簡単な例を説明します。

レギュラーモデル

この例では、次の通常のモデルを使用します。 各モデルにはデータベース テーブルが関連付けられています。

django.db からモデルをインポートします クラス Job(models.Model): name = models.CharField(max_length=200) class User(models.Model): name = models.CharField(max_length=200) class Charge(models.Model): HARDWARE = 'hw' SOFTWARE = 'sw' TYPE_CHOICES = ( (HARDWARE, 'Hardware'), (SOFTWARE, 'Software'), ) type = models.CharField(choices=TYPE_CHOICES, max_length=2) amount = models.DecimalField( max_digitals = 8、2進数 = XNUMX) month = models.DateField() user = models.ForeignKey(User) job = models.ForeignKey(Job、null=True、blank=True)

Charge モデルは、料金を保存するために使用されます。 ユーザー モデルへの外部キーと、ジョブ モデルへのオプションの外部キーがあります。 すべての Charge レコードをジョブに関連付けることができるわけではありません。

データベースビュー

データベース ビューは SQL に従って作成され、生の SQL 実行コマンドを使用してカスタマイズされたデータ移行に挿入できます。

作成または置換 VIEW app_chargesummary AS SELECT row_number() OVER () as id、ci.user_id、ci.job_id、ci.month、SUM(CASE WHEN ci.type = 'hw' THEN ci.amount ELSE 0 END) AS ハードウェア、 SUM(CASE WHEN ci.type = 'sw' THEN ci.amount ELSE 0 END) AS ソフトウェア FROM app_charge ci GROUP BY ci.user_id, ci.job_id, ci.month;

このビューでは、同じジョブ、ユーザー、月の料金レコードのすべてのハードウェア料金とソフトウェア料金が合計されます。 これには次の列が含まれます。

   [id、user_id、job_id、月、ハードウェア、ソフトウェア]

次のステップでは、このビューにマップする Django モデルを作成し、Django ORM を使用してビューからデータを取得できるようにします。

ビューのモデル

データベース ビューをマップするために以下のモデルを作成しました。

class Chargesummary(models.Model): id = models.BigIntegerField(primary_key=True) user = models.ForeignKey(User, on_delete=models.DO_NOTHING) job = models.ForeignKey(Job, on_delete=models.DO_NOTHING) month = モデル。 DateField() ハードウェア = models.DecimalField(max_digits=19, decimal_places=2) ソフトウェア = models.DecimalField(max_digits=19, decimal_places=2) クラス メタ: 管理対象 = False db_table = 'app_chargesummary'

上記のモデルは通常の Django モデルに非常によく似ていますが、注目に値する XNUMX つの違いがあります。

  1. 外部キー フィールドについては、追加の kwarg: on_delete=models.DO_NOTHING を指定する必要があります。 通常、背後のビューは書き込み可能ではないため、Chargesummary モデルで delete() を呼び出そうとすることはありませんが、この kwarg を指定しないと、一部のテスト ケースが失敗する可能性があります。 これは、テスト ケースの実行後のテスト データベースの自動クリーニング メカニズムに関連している可能性があります。
  2. South または Django データベースの自動移行がそのモデルを無視し、そのモデルのデータベース スキーマ移行の作成を試行しないように、クラス Meta で manage = False を指定する必要があります。
  3. Django ORM にどのデータベース エンティティからデータを取得するかを知らせるために、クラス Meta で db_table = 'name_of_the_view' を指定する必要があります。

新しいモデルでクエリを実行する

新しいモデルをセットアップすると、通常の Django モデルと何の違いもなく、通常の ORM クエリ メソッドを使用できます。
すべての Chargesummary オブジェクトを取得します。

Chargesummary.object.all()

2015 年 XNUMX 月のすべての Chargesummary オブジェクトを取得します。

Chargesummary.object.filter(month=datetime(year=2015, month=5, day=1, tzinfo=utc))

特定のジョブ名を持つすべての Chargesummary オブジェクトを取得します。

Chargesummary.object.filter(job__name='テスト ジョブ')

特定のユーザーの電子メール アドレスを持つすべての Chargesummary オブジェクトを取得します (ユーザー モデルに 1 対 1 のユーザー プロファイル モデルが関連付けられていると仮定します)。

Chargesummary.object.filter(user__profile__email='irwen@rescale.com')

もちろん、DML SQL は本質的にデータベース ビューに適用できないため、save() や delete() などの特定のメソッドはおそらく失敗します。

最後になりましたが

アプリを以前のバージョンから Django 1.7 以降にアップグレードする場合は、次のコマンドを実行する必要があります。

 python manage.py makemigrations your_app_label

0001_initial.py を生成し、以下のコマンドを実行してデータベースをアップグレードします。

Python manage.py 移行 --fake-initial

残念ながら、データベース ビューと管理されていない Chargesummary モデルがある場合、これは失敗します。 その理由は、Django が偽の初期フラグを使用して移行を実行するときに、0001_inital.py 移行の CreateModel 操作ですべてのモデルのテーブルがデータベースに存在することを確認するためです。 CreateModel 操作は、maneded=False のモデルも含めて、すべてのモデルに対して作成されます。 そのため、Chargesummary モデルの場合、実際のテーブルが見つからないため失敗します。 幸いなことに、view create sql を 0001_initial.py に追加し、偽の初期フラグを使用して移行を実行することで、この問題を回避できます。

... migrations.RunSQL( """ DROP VIEW IF EXISTS app_chargesummary; CREATE OR REPLACE VIEW app_chargesummary ...; """ ), ...

Django ORM を使用してモデリングとコーディングを楽しんでください。

類似の投稿