LoginSignup
14
5

More than 3 years have passed since last update.

使う:Django-MySQL

Last updated at Posted at 2019-12-05

この記事はDjango Advent Calendar 2019 5日目の記事です。

はじめに

DjangoのORMではselect_related, prefetch_related, Preftch, Qオブジェクト、などを駆使することによって開発するにあたって十二分なクエリを発行することができる。

しかし、できないこともいくつかあり、生SQLを書かなければならない、というケースもあったりする。例えば、複数のインデックスを貼っている場合、使いたいインデックスを指定する、 USE_IDNEX や、使いたくないインデックスを指定する IGNORE_INDEX など。その場合、生SQLを書く必要がある。

Django-MySQL

しかし、こちらのDjango-MySQLを使うことによって、生SQLを書かずにDjangoのORMを拡張し、 USE_IDNEXIGNORE_INDEX といったクエリを発行できる。

セットアップ

インストール

pip install django-mysql

settings.pyの編集

INSTALLED_APPSにdjango-mysqlを追加する


INSTALLED_APPS = (
    "django.contrib.admin",
    "django.contrib.auth",
    ...
    ...
    "django_mysql",
)

DJANGO_MYSQL_REWRITE_QUERIESを有効にする


DJANGO_MYSQL_REWRITE_QUERIES = True

sql_modeとinnodb_strict_modeの設定

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "HOST": "127.0.0.1",
        "NAME": "hogehoge",
        "USER": "fugafuga",
        "OPTIONS": {
            "charset": "utf8mb4",
            "init_command": "SET sql_mode='STRICT_TRANS_TABLES', innodb_strict_mode=1",
        },
    }
}

この変更による注意点として、STRICTモードを有効にすると、max_lengthを超えたデータをinsert/updateしようとするとエラーが発生する。
STRICTモードを有効にしていない場合、max_lengthを超えたデータをinsert/updateすると、超えた分を切り捨てて、エラーが発生せずに処理されるので、そこらへんのバリデーションをちゃんとやっていなかったりする場合は注意が必要。

なお、この変更はDjangoからMySQLに接続するときの設定なので、新たにmigrateをしなおすとかそういうことは必要ない。

使ってみよう

modelの設定

usersアプリケーションにhogeというモデルを作ってみる。
フィールドの fuga, piyo 両方にindexを貼る。

users/hoge.py
from django.db import models
from django_mysql.models import QuerySet


class Hoge(models.Model):
    fuga = models.IntegerField(db_index=True)
    piyo = models.IntegerField(db_index=True)

    objects = QuerySet.as_manager()

通常と違う点は from django_mysql.models import QuerySet をして、ORMのマネージャに使用するという点。
これをやることによって、Django-MySQLで拡張したクエリを発行することができる。

作られたテーブルの情報

mysql> show index from users_hoge;
+------------+------------+--------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table      | Non_unique | Key_name                 | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+--------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| users_hoge |          0 | PRIMARY                  |            1 | id          | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| users_hoge |          1 | users_hoge_fuga_8a4fba19 |            1 | fuga        | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| users_hoge |          1 | users_hoge_piyo_4539c423 |            1 | piyo        | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
+------------+------------+--------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

こんな感じです。

IGNORE INDEXしてみる

fugaのindexを使わないでクエリ発行する、というケースの場合は、以下のようにやっていく。

Hoge.objects.all().ignore_index("users_hoge_fuga_8a4fba19")

これによって発行されるSQLはこの通り

SELECT
  `users_hoge`.`id`,
  `users_hoge`.`fuga`,
  `users_hoge`.`piyo`
FROM
  `users_hoge` IGNORE INDEX(`users_hoge_fuga_8a4fba19`);

ちゃんとignore indexが発行されている。

なお、これをDjango-MySQLを使わずにやろうとすると

AttributeError: 'QuerySet' object has no attribute 'ignore_index'

このようにエラーが出る。

おわり

今の所、IGNORE INDEXしか使ったことがないけれど他にも force indexが使えたり、その他便利機能があるので、詳細はドキュメントを読んでみてください。
通常のORMでは実現できずに、苦肉の策でSQLを書かなければ、というケースが発生したときに一度、Djnago-MySQLの導入を検討してみるといいかもしれません。

余談

いつ見ても、ロゴ?というかキャラクターがやばい。
image.png

14
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
5