FIXME: 要修正
FastAPIのプロジェクトを作成した際に、パッケージのルートはどこになるんですか?
FastAPIプロジェクトを作成する際、特定の規定されたディレクトリ構造が存在するわけではありません。しかし、一般的なプロジェクト構造やベストプラクティスに基づくと、プロジェクトのルートディレクトリは通常、プロジェクト名と同じディレクトリになります。
例えば、myfastapi
という名前のFastAPIプロジェクトを作成する場合、以下のようなディレクトリ構造が考えられます:
myfastapi/
│
├── app/
│ ├── api/
│ │ ├── endpoint1.py
│ │ ├── endpoint2.py
│ │ └── ...
│ ├── models/
│ ├── schemas/
│ ├── services/
│ ├── main.py # or app.py or run.py
│ └── ...
│
├── tests/
├── .gitignore
├── pyproject.toml # または requirements.txt
└── README.md
この場合、myfastapi/
がプロジェクトのルートディレクトリになります。そして、app/
ディレクトリがアプリケーションの主要なコードを含む場所となります。
しかし、FastAPI自体はこのような構造を強制するものではなく、この構造は一例として示されています。プロジェクトの要件や好みに応じてディレクトリ構造を適切に調整することが可能です。
例えば、appをパッケージにして、apiをサブパッケージにした場合、endpoint1.pyに何かmodelsのモジュールをインポートしようとすると絶対インポートはどうなりますか?
app
をパッケージとして、その中にapi
をサブパッケージとする場合、そしてmodels
に何らかのモデル定義があると仮定すると、ディレクトリ構造は以下のようになります。
myfastapi/
│
├── app/
│ ├── api/
│ │ ├── endpoint1.py
│ │ └── ...
│ ├── models/
│ │ ├── user.py # 例として
│ │ └── ...
│ └── ...
│
└── ...
この場合、endpoint1.py
からuser.py
内のモデルを絶対インポートするには、以下のようにします。
from app.models import user
ここで、user
はapp.models
パッケージ内のuser.py
モジュールを指します。もしuser.py
内にUser
という名前のクラスが定義されている場合、以下のように具体的なクラスをインポートすることもできます。
from app.models.user import User
この方法で、endpoint1.py
内でmodels
パッケージの中のuser.py
モジュールやその中のUser
クラスを使用することができます。
なぜappがルートになるのですか?どうやって判別しているのですか?
実際には、app
がルートになっているわけではありません。プロジェクトディレクトリ myfastapi
がルートになります。ただし、Pythonのインポート機構を理解すると、なぜ app
からのインポートが機能するのかが明らかになります。
Pythonがモジュールやパッケージをインポートするとき、それを見つけるためのいくつかの場所が sys.path
というリストに列挙されています。デフォルトで、このリストには標準ライブラリやインストールされたサードパーティのライブラリの場所が含まれています。また、実行されているスクリプトのあるディレクトリも自動的にこのリストに追加されます。
たとえば、myfastapi
ディレクトリで FastAPI アプリケーションを実行すると、そのディレクトリが sys.path
に追加されます。これにより、Pythonは app
やその他のサブディレクトリやモジュールを見つけることができます。したがって、app
から始まるインポートが可能になります。
しかし、例えば app
ディレクトリ内で直接 Python スクリプトを実行すると、その場合 app
ディレクトリ自体が sys.path
に追加されるため、インポートの際には app
を指定せずに他のサブモジュールやサブパッケージをインポートする必要があります。
この振る舞いを確実にしたい場合は、プロジェクトルートに __init__.py
を置いて、ルートを明示的なパッケージとして扱うという手も考えられますが、通常の FastAPI プロジェクトではこのような構造は推奨されません。
要するに、Pythonのインポートの動作は、実行コンテキストと sys.path
の現在の内容に強く依存していると言えます。
なるほど、じゃあmain.pyがappにある場合は、app ディレクトリ自体が sys.path に追加されるから、絶対インポートの先頭がappではなくなりますか?
はい、正確にはその通りです。
もし main.py
が app
ディレクトリの中に存在していて、その main.py
を実行する場合、app
ディレクトリが sys.path
に自動的に追加されます。したがって、app
ディレクトリを起点としてインポートが行われます。
具体的なディレクトリ構造が以下のような場合:
myfastapi/
│
├── app/
│ ├── api/
│ │ ├── endpoint1.py
│ │ └── ...
│ ├── models/
│ │ └── ...
│ ├── main.py
│ └── ...
│
└── ...
この状況で、app/main.py
を実行すると、インポート文は以下のようになります:
from api import endpoint1 # 例
from models import user # 仮にuser.pyがmodelsの中に存在する場合
この場合、app
をインポートのパスに含める必要はありません。なぜなら、現在の実行ディレクトリ(app
ディレクトリ)が sys.path
に自動的に追加されるからです。
> sys.path に自動的に追加される これはFastAPIの機能ですか?
いいえ、これはFastAPIの機能ではありません。sys.path
への現在の実行ディレクトリの追加はPython自体の動作によるものです。
Pythonを起動すると、Pythonは実行されているスクリプトのあるディレクトリを自動的に sys.path
の先頭に追加します。この動作は、スクリプトがどのフレームワークやライブラリを使っているかに関係なく、Python自体のデフォルトの動作として行われます。
したがって、あるディレクトリでPythonスクリプトを実行すると、そのディレクトリは自動的にモジュールの検索パスに含まれることになります。