DIVX テックブログ

catch-img

Djangoで開発を行う際にやっておいて良かったこと

はじめに

案件内でDjangoでの開発環境の構築を行った際にやっておいて良かったことを紹介しようと思います。

開発環境は下記を利用しています。

Django==3.2.5
djangorestframework==3.12.4
factory-boy==3.3.0
pytest==7.4.3

※開発当時のバージョンです

settings.pyの分割

ローカル環境と本番環境で異なる設定が必要であり、環境変数だけではカバーしきれないものがあったため、設定ファイルを環境ごとに分割しました。

一例として、本番環境の設定ファイルではDjango REST frameworkのブラウザのGUIコンソールを非表示にする設定を記述しています。

if文を使って異なる設定を行うことも可能ですが、if文が増えてくると可読性が落ちたりメンテナンスがしずらいと思ったため、今回は設定ファイル分割の方法を選択しています。

やり方

  • settings.pyがある場所にsettings ディレクトリを用意する
  • settingsディレクトリに各環境の設定ファイルを用意する
    • ローカル環境、検証環境、本番環境が存在している場合は以下のような形になる
    • ディレクトリをPythonパッケージとして扱うために、__init__.pyファイルも用意する
settings/
 ├ __init__.py
 ├ base.py
 ├ development.py
 ├ local.py
 └ production.py
  • 環境変数で、DJANGO_SETTINGS_MODULE="myproject.settings.local”と設定することで、local.pyが読み込まれるようになる

全環境共通の設定ファイルも作成可能です。今回はbase.pyと名付けています。

各環境の設定ファイル内で以下の記述を行うことで、共通設定を読み込めます。

from .base import *

models.pyの分割

多くのテーブルを扱うとmodels.pyが肥大化しがちです。

今回は80個ほどのテーブルがあったため、可読性向上を目的にテーブルごとにファイルを作成しました。

やり方

  • models.pyは削除し、models.pyがあった場所にmodels ディレクトリを用意する
  • modelsディレクトリに各テーブルのファイルを用意する
    • 各テーブルのファイルの中身はmodels.pyと同じ書き方で問題ない
models/
 ├ __init__.py
 ├ chat_group.py
 └ chat.py
  • modelsディレクトリに__init__.py ファイルを用意する
  • __init__.py ファイルで各テーブルのファイルをimportする
from .chat import Chat
from .chat_group import ChatGroup
  • 通常通りpython manage.py makemigrationspython manage.py migrate を実行

fixtureの利用

マスターデータなどの投入をする際、fixtureを利用すると簡単に行えます。

今回は、チーム開発かつマスターデータが複数テーブルに存在していたため、fixtureを利用しました。

新しく入ってきたメンバーのローカル環境構築時に、自分のローカル環境のダンプデータを渡す手間が省けるので便利でした。

ただし、カラム等の設定が更新された際は、fixtureも更新する必要がある点には注意してください。

やり方

  • fixturesディレクトリを作成する
    • modelsやviewsと同じ階層に作成
app/
 ├ fixtures/
 ├ models/
 └ views/
  • fixturesディレクトリにjsonファイルを作成する
// sample.json
[    
		{
        "model": "app.chat", //モデル名
        "pk": 1,
        "fields": {
            "name": "花子",
            "context": "こんにちは"
        }
    },
		{
        "model": "app.chat", //モデル名
        "pk": 2,
        "fields": {
            "name": "太郎",
            "context": "さようなら"
        }
    }
]
  • python manage.py loaddata {ファイル名}.json コマンドを実行することで、DBにデータ投入される

python manage.py dumpdata {ファイル名}.json を実行すると、同じ形式でデータが出力されます。

今回はJSON形式を紹介しましたが、YAMLやXML形式のサポートもあります。

他の形式での書き方については、公式ドキュメントを参照ください。

https://docs.djangoproject.com/en/3.2/howto/initial-data/

SQLのログを表示する

N+1問題が発生していないか簡単に確認できるよう、発行されたSQLをログに出力するよう設定を行いました。

様々なデバッグ情報を表示してくれるDjango Debug Toolbar を使いN+1問題が発生していないか確認する方法もあります。必要に応じてこちらを採用するのも良いかと思います。

私は開発中にログを流しておくタイプなので、ログに出しておくだけでも結構N+1に気づくことができました。

やり方

  • settings.pyに以下を記述
# 発行されたSQLをログに出力
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "console": {
            "level": "DEBUG",
            "class": "logging.StreamHandler",
        },
    },
    "loggers": {
        "django.db.backends": {
            "handlers": ["console"],
            "level": "DEBUG", //必要に応じて"INFO"や"WARNING"などに変更
        },
    },
}

ログレベルや出力フォーマットのカスタムなども可能です。詳しい方法は公式ドキュメントを参照ください。

https://docs.djangoproject.com/en/3.2/topics/logging/

factory_boyの導入

テストコードを書く際に、factory_boyを利用しました。

factory_boyを利用することより、テスト用データの作成が容易になります。

後述しますが、自動でリレーション先のデータをよしなに作成してくれる機能があるため、整合性の取れたテストデータを用意するのが楽になった印象です。

テストフレームワークはPytestを使用しています。

やり方

  • factory_boyをインストールする
pip install factory_boy
  • testsディレクトリの下にfactoriesディレクトリを用意する
tests/
 ├ factories/
 └    └ chat_factory.py
  • factoriesディレクトリに各テーブルのファイルを用意する

factoryの書き方も少し載せておきます。

from app.tests.factories import ChatGroupFactory
STATUS = [1, 2, 3]

class ChatFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Chat

    chat_group = SubFactory(ChatGroupFactory)
    content = "テキストサンプルです。"
    sample_number = Sequence(lambda n: n)
    status = fuzzy.FuzzyChoice(STATUS)

リレーションがある場合にこのような書き方すると、自動でリレーション先のデータも作成してくれる

fuga = SubFactory(ChatGroupFactory)

データを複数作成した時に、nに通し番号を入れてくれる

sample_number = Sequence(lambda n: n)

配列の中身の中からランダムで1つ選んで設定してくれる

status = fuzzy.FuzzyChoice(STATUS)

呼び出し方は以下

chat = ChatFactory() // factoryファイルに書いた値で生成される
print(chat.content) // テキストサンプルです。


chat2 = ChatFactory(content="別のテキストです") // factoryファイルに書いている値ではないもので生成も可能
print(chat2.content) // 別のテキストです


chat_group = ChatGroupFactory()
ChatFactory(chat_group=chat_group)

公式ドキュメントでもサンプルが紹介されています。

https://factoryboy.readthedocs.io/en/stable/examples.html

リンター・フォーマッター導入

コードの一貫性を保つために、リンター・フォーマッターを導入しました。

今回はPEP8に準拠したかったため、flake8とblackを採用しています。

また、GitLabのCI/CDでflake8とblackのコマンドを実行し、チェックを自動的に行うようにしています。

やり方

  • インストールする
pip install flake8
pip install black​​​​​​​
  • .flake8の更新
    • Flake8では1行の最大文字数を79文字に規定している一方で、Blackでは88文字まで許容しているため、この競合を回避するためmax-line-lengthを88に設定
    • flake8ではコロンの前に空白がある場合に警告を出す(E203)が、black は算術演算子の周りの空白を許可するため、この競合を回避するためE203を無視
[flake8]
//必要に応じて他の設定も行う
max-line-length = 88
extend-ignore = E203

// 対象外ファイルの設定。必要に応じて設定
exclude = app/fixtures, app/migrations
  • pyproject.tomlの更新
[tool.black]

// 対象外ファイルの設定。必要に応じて設定
exclude = '''
/(
    app/fixtures
  | app/migrations
)/
'''
  • コマンドの実行
    • flake8 --show-source {対象ディレクトリ}
      • 例)flake8 --show-source app
    • black {対象ディレクトリ}
      • 例)black app

さいごに

Djangoで開発を行う際にやっておいて良かったことを紹介してきました。

どれも簡単に行えるものなので、初期段階でささっと行っておくのが良さそうだと個人的に思いました。

Djangoでの開発の助けになれば幸いです。

お悩みご相談ください

  ご相談フォーム | 株式会社divx(ディブエックス) DIVXのご相談フォームページです。 株式会社divx(ディブエックス)


お気軽にご相談ください


ご不明な点はお気軽に
お問い合わせください

サービス資料や
お役立ち資料はこちら

DIVXブログ

テックブログ タグ一覧

人気記事ランキング

GoTopイメージ