Django ORMのクエリ最適化とAJAXによる非同期通信の実装

ORMクエリの最適化戦略

データベースへの不要なアクセスを削減し、パフォーマンスを向上させるための手法として、Django ORMは複数の最適化メソッドを提供しています。ここでは、主要な4つのメソッドについて解説します。

only()とdefer()の使い分け

only()メソッドは、指定したフィールドのみを取得対象とします。取得結果のオブジェクトから、指定したフィールドにアクセスする場合は追加のクエリが発生しませんが、指定外のフィールドにアクセスすると新たにクエリが実行されます。

# タイトルのみ取得
books = Book.objects.only('title')

for book in books:
    print(book.title)  # クエリ発行なし
    print(book.price)   # 各ループでクエリ発行

defer()はonly()と逆の動作をします。指定したフィールドを遅延読み込みの対象とし、それ以外のフィールドは即座に取得されます。

# タイトル以外を取得
books = Book.objects.defer('title')

for book in books:
    print(book.price)   # クエリ発行なし
    print(book.title)   # 各ループでクエリ発行

select_related()とprefetch_related()の違い

select_related()は、外部キー関係(一対一、多対一)に対してJOINを行い、単一のクエリで関連データを取得します。

# INNER JOINで一括取得
books = Book.objects.select_related('publisher')

for book in books:
    print(book.publisher.name)  # 追加クエリなし

prefetch_related()は、多対多関係や逆参照に対して使用し、複数のクエリを発行した後にPythonレベルで結合を行います。

# 複数クエリで取得後に結合
books = Book.objects.prefetch_related('authors')

for book in books:
    for author in book.authors.all():
        print(author.name)  # 追加クエリなし

大きなテーブルを扱う場合、select_related()はJOINの負荷が高くなる可能性があるため、状況に応じて使い分けることが重要です。

MTVとMVCアーキテクチャ

DjangoはMTV(Model-Template-View)アーキテクチャを採用していますが、これは本質的にMVC(Model-View-Controller)パターンと同じ概念です。

  • Model: データベースとのやり取りを担当
  • Template: ユーザーインターフェースの表示(MVCのViewに相当)
  • View: ビジネスロジックの処理(MVCのControllerに相当)

choicesパラメータの活用

性別やステータスなど、選択肢が限定的なフィールドに対してchoicesパラメータを使用すると、データベースには数値を保存しつつ、表示時には対応する文字列を取得できます。

class Member(models.Model):
    STATUS_CHOICES = [
        (1, 'アクティブ'),
        (2, '非アクティブ'),
        (3, '退会'),
    ]
    status = models.IntegerField(choices=STATUS_CHOICES)

# 使用例
member = Member.objects.get(pk=1)
print(member.get_status_display())  # "アクティブ"と表示

get_<フィールド名>_display()メソッドで対応する文字列を取得できます。定義されていない値が保存されている場合、その値がそのまま返されます。

AJAXの基礎と実装

AJAX(Asynchronous JavaScript And XML)を使用すると、ページ全体をリロードすることなく、サーバーとデータをやり取りし、ページの一部を更新できます。

基本的なAJAXリクエスト

$('#calculate').click(function() {
    $.ajax({
        url: '/api/calculate/',
        type: 'POST',
        data: {
            'num1': $('#input1').val(),
            'num2': $('#input2').val()
        },
        success: function(response) {
            $('#result').val(response);
        }
    });
});

JSONデータの送信

JSON形式でデータを送信する場合、contentTypeを指定し、データをシリアライズする必要があります。

$.ajax({
    url: '/api/data/',
    type: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({
        'username': 'user1',
        'password': 'pass123'
    }),
    success: function(response) {
        console.log(response);
    }
});

Django側ではrequest.bodyからJSONデータを取得します。

import json
from django.http import JsonResponse

def api_view(request):
    if request.is_ajax():
        data = json.loads(request.body.decode('utf-8'))
        return JsonResponse(data)
    return render(request, 'index.html')

ファイルのアップロード

FormDataオブジェクトを使用して、ファイルとテキストデータを同時に送信できます。

$('#upload').click(function() {
    let formData = new FormData();
    formData.append('username', 'user1');
    formData.append('avatar', $('#fileInput')[0].files[0]);

    $.ajax({
        url: '/api/upload/',
        type: 'POST',
        data: formData,
        contentType: false,
        processData: false,
        success: function(response) {
            alert('アップロード完了');
        }
    });
});

Django側ではrequest.FILESでファイルデータを受け取ります。

シリアライズ機能

Djangoのserializersモジュールを使用すると、クエリセットをJSON形式に変換できます。

from django.core import serializers

def export_data(request):
    members = Member.objects.all()
    json_data = serializers.serialize('json', members)
    return HttpResponse(json_data, content_type='application/json')

出力形式は以下のようになります:

[
    {"model": "app.member", "pk": 1, "fields": {"name": "田中", "status": 1}},
    {"model": "app.member", "pk": 2, "fields": {"name": "佐藤", "status": 2}}
]

タグ: Django ORM AJAX Python データベース最適化

6月28日 18:41 投稿