はじめに
ちょっとQuerySetの動きをみたいとか、特定のコードを動かしたいとかあるよね?Django Shellでもいいのだけど、色々試したいからちょっと面倒なのでJupyter Notebookで動かしたい。 前職でも似たようなことをやっていてその時のスニペットをメモしていなくて地味に困っていたのだけど、時間ができたので再度作成しました。 もっと短かった気もするのだけど、とりあえず動くのでOK
コード
import os import django # https://docs.djangoproject.com/en/3.2/topics/async/#envvar-DJANGO_ALLOW_ASYNC_UNSAFE os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true" os.environ.setdefault("DJANGO_SETTINGS_MODULE", "api.settings") django.setup()
上記のコードで設定が完了。次のセルでQuerySetなどが動きます。
from your_app_name.models import YourModelName YourModelName.objects.all()
キャプチャ
DJANGO_ALLOW_ASYNC_UNSAFE
について
- コード上にコメント入れているけど、
DJANGO_ALLOW_ASYNC_UNSAFE
を有効にしています - あくまでも開発時に少し試したいという時のコードなので問題はないはず
- 詳細はドキュメント参照
- 「はじめに」にも書いたけど前職のときのスニペットはこれは設定していなかった気がする
- 設定しない場合には以下のエラーが発生する(ドキュメントの通り)
ログ全文はこちらクリック
--------------------------------------------------------------------------- SynchronousOnlyOperation Traceback (most recent call last) Cell In[1], line 12 8 django.setup() 10 from api.modules.user.models import User ---> 12 User.objects.filter(email="a@example.com").exists() File /usr/local/lib/python3.9/site-packages/django/db/models/query.py:808, in QuerySet.exists(self) 806 def exists(self): 807 if self._result_cache is None: --> 808 return self.query.has_results(using=self.db) 809 return bool(self._result_cache) File /usr/local/lib/python3.9/site-packages/django/db/models/sql/query.py:550, in Query.has_results(self, using) 548 q = self.exists(using) 549 compiler = q.get_compiler(using=using) --> 550 return compiler.has_results() File /usr/local/lib/python3.9/site-packages/django/db/models/sql/compiler.py:1145, in SQLCompiler.has_results(self) 1140 def has_results(self): 1141 """ 1142 Backends (e.g. NoSQL) can override this in order to use optimized 1143 versions of "query has any results." 1144 """ -> 1145 return bool(self.execute_sql(SINGLE)) File /usr/local/lib/python3.9/site-packages/django/db/models/sql/compiler.py:1173, in SQLCompiler.execute_sql(self, result_type, chunked_fetch, chunk_size) 1171 cursor = self.connection.chunked_cursor() 1172 else: -> 1173 cursor = self.connection.cursor() 1174 try: 1175 cursor.execute(sql, params) File /usr/local/lib/python3.9/site-packages/django/utils/asyncio.py:24, in async_unsafe.<locals>.decorator.<locals>.inner(*args, **kwargs) 22 else: 23 if event_loop.is_running(): ---> 24 raise SynchronousOnlyOperation(message) 25 # Pass onwards. 26 return func(*args, **kwargs) SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.
sync_to_async
を使うと DJANGO_ALLOW_ASYNC_UNSAFE
が不要
import os import django # type: ignore from asgiref.sync import sync_to_async # type: ignore os.environ.setdefault("DJANGO_SETTINGS_MODULE", "api.settings") django.setup() from api.modules.user.models import User @sync_to_async def check_user_exists(): return User.objects.filter(email="a@example.com").exists() await check_user_exists()
上記のように sync_to_async
を使用することで同期を正しくとって安全に動かすことも可能。っというかこちらがあるべきの実装になる。が、繰り返しになるが今回は開発時など一時的に使用することを目的とするため、こちらはあまり使うことはないかなと思った次第です。