1.

DjangoのMySQL/MariaDB接続|設定・認証とmigrateを分ける

▶

DjangoからMySQL/MariaDBへ接続するには、使うPython環境・driver・DB aliasと有効設定を確認します。接続確認と、migrateによるschema変更は別作業です。この記事の新しい例はDjango5.2.18/mysqlclient2.2.7/MySQL8.4.13の隔離検体で確認しています。

djangoの有効設定と接続先・認証を確認し、接続確認とschema変更を分ける図

実行環境とdriverを先に確認する

同じpythonコマンド名でも、venv/実行プロセスによって導入済みdriverは異なります。Djangoの組込みMySQLバックエンドにはmysqlclient用のadapterがあります。MySQL Connector/Pythonは別adapterの対応条件を確認します。PyMySQLのinstall_as_MySQLdb()があるだけで全Django版/認証/型/TLSまで互換と判断せず、バージョン情報を偽って要件検査を回避しません。今回はPyMySQLを追加導入/実行していません。

明示した環境変数からローカル検体へ設定する例

次は既にある検体DB/専用ユーザーへ接続するための部分設定です。完全な本番settings.pyではなく、既存プロジェクト全体を置換しません。DB_NAME/DB_USER/DB_PASSWORD/DB_HOST/DB_PORTを対象環境で明示し、実パスワードをソースへ書きません。変数がない場合に偶然別DBへ接続しないよう、既定host等へフォールバックしない例です。環境変数に置くだけで秘密の保護が完成するわけではありません。

import os

# Local connection probe only, not a complete production settings/TLS policy.
# Use the intended environment; do not put real credentials in public source.
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": os.environ["DB_NAME"],
        "USER": os.environ["DB_USER"],
        "PASSWORD": os.environ["DB_PASSWORD"],
        "HOST": os.environ["DB_HOST"],
        "PORT": os.environ["DB_PORT"],
        "CONN_MAX_AGE": 0,
        "OPTIONS": {"charset": "utf8mb4", "connect_timeout": 5},
    }
}

OPTIONSは別の接続値を上書きし得ます。read_default_fileや別aliasもあるため、settingsのNAME等だけで実接続先を決めつけません。charset=utf8mb4は接続の文字コードで、既存表/列の文字コードや照合順序を変更しません。sql_modeを単一値へ上書きして他の規則を落とさず、既存値/要件を確認します。

migrateせずに接続を調べる

許可されたDjango shell等で次の関数を定義し、print(probe_connection())を実行する例です。default以外を確認する場合は承認済みのaliasを明示します。DB名・認証アカウント・版・sql_modeを読み、例外の生メッセージやパスワードは出しません。出力のhost/user等もそのまま公開しません。

from django.db import DatabaseError, connections


def probe_connection(alias="default"):
    # Authorized management context only; do not publish host/user metadata.
    connection = connections[alias]
    if connection.vendor != "mysql":
        raise ValueError("Select the intended MySQL/MariaDB alias")
    params = connection.get_connection_params()
    result = {
        "alias": alias,
        "host": params.get("host"),
        "port": params.get("port"),
        "database": params.get("database"),
        "username": params.get("user"),
        "password_configured": bool(params.get("password")),
    }
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                "SELECT VERSION(), DATABASE(), CURRENT_USER(), @@SESSION.sql_mode"
            )
            version, database, account, mode = cursor.fetchone()
        result.update(ok=True, server_version=version, database_name=database,
                      authenticated_account=account, sql_mode=mode)
    except DatabaseError as error:
        # Do not expose SQL, full driver messages, URLs or credentials.
        result.update(ok=False, exception=type(error).__name__,
                      mysql_code=error.args[0] if error.args else None)
    finally:
        connection.close()
    return result

この関数は明示したaliasのcursorを使い、実ORMのルーター全経路やレプリカの整合性を確認しません。読取成功と、業務処理/書込/マイグレーションの権限は別です。新しい検体ではSELECTが成功してもINSERT/CREATE TABLEは拒否され、表・データ・権限の前後不変を確認しました。

認証・接続・設定SQLの失敗を区別する

1045の認証失敗を、表への操作権限不足やDB名不一致と混同しません。2002等も番号だけで全原因を決めず、実際のメッセージ・ホスト/ソケット/ポートを確認します。Unknown databaseを見て新DBを自動作成しません。旧本番例のSET sql_mode=..., NAMES ...はこの検体では受理され、カンマがあるだけで構文エラーとは扱いません。一方、存在しない変数を使う検体のinit_commandは1193で失敗しました。設定SQLの失敗を認証失敗と即断せず、実際の命令と対象版を確認します。

本番TLS・常駐接続・読み書き分離の範囲

新しい例はローカル検体用で、本番TLS方針は含みません。旧例にca/cert/keyが書いてあってもサーバー証明書/ホスト名の検証成功まで保証しないでください。driver/クライアントライブラリ/接続経路ごとに確認し、検証を無効にしません。

CONN_MAX_AGEは接続の再利用期間で、接続プールの導入とは同じではありません。600秒を全環境の最適値とせず、ASGIではDjangoの持続接続の注意も確認します。ルーター例の全readをreplicaへ送る設計には複製遅延やread-after-write、トランザクション/関係の制約があり、allow_relation=Trueだけで別DBの参照整合性が得られません。実replication/ASGI/TLSは今回未検証です。

以前の例と補足

以下の章/コードを保持し、driver互換性や接続確認の誤った説明は訂正しています。公開パスワード、GRANT ALL、%の追加、FLUSH、migrateを接続失敗に対する一連の操作として実行しません。

旧例の注意:組込みbackend用driverと他driverの対応条件を区別しています。公開サンプルへ本番設定を一括置換しません。

この記事の要点
  • Django から MySQL / MariaDB に接続する設定方法
  • driver: 組込みbackendのmysqlclient用adapterと、他driverの対応条件を区別
  • settings.py の DATABASES で ENGINE を django.db.backends.mysql
  • NAME / USER / PASSWORD / HOST / PORT を本番値に合わせる
  • 文字コードは OPTIONS で charset=utf8mb4 を指定(絵文字対応)

前提

  • MySQL / MariaDB がインストール済み・起動中
  • データベースとユーザーが作成済み
  • Django プロジェクトが既に django-admin startproject 済み

1. パッケージインストール

Django から MySQL に繋ぐにはドライバが必要です:

旧例の注意:OS用コマンドは選択肢で一括実行しません。実Python/ABI/OSでwheelやbuild要件を確認し、mysqlclient/PyMySQL両方の導入を必須にしません。

# 推奨: mysqlclient (C 実装で高速)
pip install mysqlclient

# Ubuntu / Debian で C ヘッダが必要
sudo apt install python3-dev default-libmysqlclient-dev build-essential pkg-config

# Mac (Homebrew)
brew install mysql pkg-config

# Windows は wheel が公式配布されているので普通に
pip install mysqlclient

# 代替: pymysql (Pure Python、ビルド不要)
pip install pymysql

pymysql を使う場合の追加設定

次のPyMySQL shimは旧構成の記録です。全Django版や認証/型/TLSの互換を保証せず、要件検査を偽って回避しません。新しい検証はmysqlclientを使っています:

旧例の注意:旧PyMySQL shimです。互換確認済みと扱わず、version_infoを書き換えて検査を回避しません。manage.py/wsgi.pyへ一律に追加する推奨でもありません。

# myproject/__init__.py (プロジェクトのルート)
import pymysql
pymysql.install_as_MySQLdb()

2. settings.py の DATABASES 設定

旧例の注意:2つのDATABASES定義は代替例です。公開パスワードを使わず、OPTIONSの優先やsql_mode上書きの影響も調べます。環境変数だけで秘密保護が完成したとは扱いません。

# myproject/settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mydb',
        'USER': 'myuser',
        'PASSWORD': 'mypassword',
        'HOST': '127.0.0.1',       # またはサーバ IP / ホスト名
        'PORT': '3306',
        'OPTIONS': {
            'charset': 'utf8mb4',
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
        },
    }
}

# 環境変数経由が安全
import os
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': os.environ['DB_NAME'],
        'USER': os.environ['DB_USER'],
        'PASSWORD': os.environ['DB_PASSWORD'],
        'HOST': os.environ.get('DB_HOST', '127.0.0.1'),
        'PORT': os.environ.get('DB_PORT', '3306'),
        'OPTIONS': {
            'charset': 'utf8mb4',
        },
    }
}

3. データベースとユーザーを作成

旧例の注意:shellのmysql起動とSQLが混在する旧記録です。広いGRANT/公開パスワード/%追加を実行せず、通常のCREATE USER/GRANT後のFLUSHは不要です。

-- MySQL に root でログイン
mysql -u root -p

-- DB 作成(utf8mb4 で絵文字対応)
CREATE DATABASE mydb
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

-- ユーザー作成
CREATE USER 'myuser'@'localhost' IDENTIFIED BY 'mypassword';

-- 権限付与(DB 全体)
GRANT ALL PRIVILEGES ON mydb.* TO 'myuser'@'localhost';

-- リモート接続も許可するなら(本番では注意)
CREATE USER 'myuser'@'%' IDENTIFIED BY 'mypassword';
GRANT ALL PRIVILEGES ON mydb.* TO 'myuser'@'%';

-- 反映
FLUSH PRIVILEGES;
\q

4. マイグレーション実行

旧例の注意:migrateはschemaを変更する作業で、接続確認ではありません。新しい読取例を先に使い、既存本番DBへ接続試験のために実行しません。

# 接続確認 + 標準テーブル作成
python manage.py migrate

# 出力例:
# Operations to perform:
#   Apply all migrations: admin, auth, contenttypes, sessions
# Running migrations:
#   Applying contenttypes.0001_initial... OK
#   ...
#   Applying sessions.0001_initial... OK

# 接続成功!

5. 動作確認

旧例の注意:dbshellには外部クライアント等も必要です。Python側の接続成功とCLI経路を区別し、表示8.0.36を実測値や全環境共通としません。

# シェルから直接 SQL を叩く
python manage.py dbshell
mysql> SHOW TABLES;
mysql> exit

# Django shell から
python manage.py shell
>>> from django.db import connection
>>> with connection.cursor() as c:
...     c.execute("SELECT VERSION()")
...     print(c.fetchone())
...
('8.0.36',)

本番用の追加設定

旧例の注意:600秒は全環境の推奨値ではなく、ASGI/driver/サーバー上限も確認します。TLSのca/cert/key記述だけでホスト名検証を保証しません。組合せSETは今回受理されましたが、sql_modeの他の規則を落とさず実値/要件を確認します。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': os.environ['DB_NAME'],
        'USER': os.environ['DB_USER'],
        'PASSWORD': os.environ['DB_PASSWORD'],
        'HOST': os.environ['DB_HOST'],
        'PORT': os.environ.get('DB_PORT', '3306'),

        # コネクション維持(最大 600 秒)
        'CONN_MAX_AGE': 600,

        # SSL 接続
        'OPTIONS': {
            'charset': 'utf8mb4',
            'ssl': {
                'ca': '/etc/mysql/ca.pem',
                'cert': '/etc/mysql/client-cert.pem',
                'key': '/etc/mysql/client-key.pem',
            },
            # 文字コード周りの厳密設定
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES', "
                            "NAMES utf8mb4 COLLATE utf8mb4_unicode_ci",
        },
    }
}

# 接続プール(django-db-connection-pool 等を利用)
# pip install django-db-connection-pool

複数 DB の同居(読み書き分離など)

旧例の注意:ルーターの形を示す旧例で、replicaの遅延/整合性/障害を解決しません。全readや全relationを無条件許可せず、異なるDB間のFKを保証しません。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mydb_master',
        'HOST': 'mysql-master.example.com',
        # ...
    },
    'replica': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mydb_replica',
        'HOST': 'mysql-replica.example.com',
        # ...
    },
}

DATABASE_ROUTERS = ['myapp.routers.MasterReplicaRouter']

# myapp/routers.py
class MasterReplicaRouter:
    def db_for_read(self, model, **hints):
        return 'replica'
    def db_for_write(self, model, **hints):
        return 'default'
    def allow_relation(self, obj1, obj2, **hints):
        return True
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        return db == 'default'

よくあるエラー

旧例の注意:原因候補を限定し過ぎていたセルを訂正しています。新DB作成/GRANT/OS依存導入やcharset一括変換を自動対処にしません。

エラー原因対処
Can't connect to MySQL serverMySQL 未起動 / ポート不一致systemctl status mysql
Access denied for user有効設定/対象サーバー/認証条件を確認。SQL操作権限は別1045の原因を調べ、全権限や%を自動追加しない
Unknown database 'xxx'意図した接続先/DB名と存在・権限を確認名前を直さず新規DBを自動作成しない
NameError: name '_mysql' is not defineddriverのimport/実体/依存ライブラリ等を確認メッセージ/対象Python/OSを照合し、一律のapt導入へ進まない
django.db.utils.OperationalError: (2002, ...)host/socket/port等の接続経路を確認番号だけで原因を限定せず、意図した経路で比較
(1366, "Incorrect string value")データの符号化/接続/列型等を確認実データと列/接続を照合し、既存DBの一括変換は別途計画

utf8mb4 が必須な理由

  • MySQL の utf8 は実は 3 バイト UTF-8 だけ → 絵文字(4 バイト)が入らない
  • utf8mb4 が真の UTF-8(4 バイト)— 絵文字も OK
  • 既定charset/utf8の別名は製品・版・設定で異なるため、実値を確認する
  • DB/表/列/接続それぞれを調査する。接続のcharsetだけで既存列や照合順序は変わらない

関連

  • django-environ: DATABASE_URL=mysql://user:pass@host/db 形式の環境変数を自動展開
  • dj-database-url: 同上のパッケージ
  • django-db-geventpool: 非同期環境用接続プール
  • Django Migrations: モデル変更を SQL に変換

参考:旧構成の書式付き例

元例の注意:元の導入例ですが、両driverをまとめて導入する推奨ではありません。実Python/Django/driverの対応を確認します。

pip install pymysql

pip install mysqlclient

元例の注意:元の書式付きsettings例です。PyMySQL shimやSQLite ENGINEの別製品例をMySQLへ一律適用せず、現在の接続条件と要件を確認します。

settings.py

import pymysql

pymysql.install_as_MySQLdb()

 

...

 

DATABASES = {

    'default': {

        'ENGINE': 'django.db.backends.mysql',

        'NAME': 'db_name',  # DB名

        'USER': 'user',  # DBユーザー名

        'PASSWORD': 'pass',  # パスワード

        'HOST': 'localhost',  # ホスト名

        'PORT': '3306',  # ポート

        'OPTIONS': {

                'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",

                },    

        }
}

元例の注意:dbshellは外部mysqlクライアントも必要です。driver導入だけで使えると断定せず、実行環境/alias/接続経路を確認します。

python manage.py dbshell

確認範囲と関連ページ

2026年10月26日確認。PHP8.5.12/Laravel13.34.0のvendor bootstrap/PDOとPython3.12.10/Django5.2.18/mysqlclient2.2.7を、新規ローカルMySQL8.4.13の検体にだけ接続しました。新しい掲載ソースの読取、認証1045と操作拒否1142、設定の優先関係、schema/データ/権限の前後不変を確認しています。Laravel全アプリのbootstrap/HTTP/FPM/worker、PyMySQL/MariaDB/全版、実TLS/ASGI/レプリカ、実環境の秘密管理/本番配備は未検証です。CWiki本番のDB設定/認証/権限/接続先を変更せず、config:clearやmigrateを実行していません。

データベースへの接続と操作の子ページから選択できます。MySQL/MariaDBの親ページとDjangoの接続エラーも参照します。

一次資料:Laravelの外部環境変数と設定キャッシュ、Laravelの接続設定とURL解析、Django5.2のdriver/接続/持続接続、Djangoのaliasとルーターの制限、MySQLの接続エラーとpassword表示、MySQL SET文。

子ページ

子ページはありません

同階層のページ
  1. MySQL/MariaDBへの接続
  2. sqliteへの接続
  3. SELECT, INSERT, UPDATE, DELETE
  4. 素のSQLを直接実行する方法
  5. Order by DESCの指定方法
  6. limit, offsetの指定方法
  7. filterの検索オプション
  8. django-filterのlookup_expr検索オプション
  9. モデルの内部結合(1対1)

最近更新/作成されたページ