【解決】JupyterLabからデータを生成しようとするとSynchronousOnlyOperationエラーが起きる

JupyterLabからテスト用のデータを作成しようとしてエラーになったのでメモ。
以下のようなテスト用のデータを生成するコードをJupyterLabで実行。

import random, string
import uuid
import datetime

def randomname(n):
   randlst = [random.choice(string.ascii_letters + string.digits) for i in range(n)]
   return ''.join(randlst)

data = {
    "id"               : str(uuid.uuid4())
    ,"title"           : randomname(100)
    ,"created_at"      : datetime.datetime.now()
    ,"last_update"     : datetime.datetime.now()
    ,"isdraft"         : False
    ,"thumbnail"       : random.randint(0,10000)
    ,"text"            : randomname(10000)
    ,"likes"           : random.randint(0,100)
}

TopicsTr.objects.create(**data)

しかし、以下のようなエラーが発生してうまくいかない。

---------------------------------------------------------------------------
SynchronousOnlyOperation                  Traceback (most recent call last)
<ipython-input-6-d242e32e8454> in <module>
     19     ,"likes"           : random.randint(0,100)
     20 }
---> 21 TopicsTr.objects.create(**data)

/usr/local/python/lib/python3.8/site-packages/django/utils/asyncio.py in 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.


SynchronousOnlyOperation ??
初めて聞くエラー内容だけどDjango3.0からの仕様かなぁと思い調べていると以下の記事を見つけました。
ryu22e.org


どうもORMでの操作は非同期では安全ではないらしく実行が制限されてしまうようです。
ただ、マニュアルを読んでいると以下のような記載がありました。

If you are absolutely in dire need to run this code from an asynchronous context - for example, it is being forced on you by an external environment, and you are sure there is no chance of it being run concurrently (e.g. you are in a Jupyter notebook), then you can disable the warning with the DJANGO_ALLOW_ASYNC_UNSAFE environment variable.


まさかのJupyter名指しですね(笑)
読む限りだとJupyter Notebookのように外部から強制的に実行させられていて、同時に実行される可能性がないものについては
DJANGO_ALLOW_ASYNC_UNSAFE環境変数を使って、警告を無効にできるみたいです。
早速設定してみます。

先ほどJupyterで書いたコードの先頭付近に以下の一行を追加します。

os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"


そして実行すると・・・

f:id:fclout:20200426212140p:plain


うまくいきましたね。
念のため、テーブルにデータが追加されているかどうか確かめてみると

mysql> select count(*) from BlogManager_topicstr;
+----------+
| count(*) |
+----------+
|        1 |
+----------+
1 row in set (0.01 sec)

ちゃんとデータが追加されていました。
Jupyterを使って作業する際はsetup()同様に必須コードになりそうです。