知識の枝

"All is well"

Django CharFieldにランダムな文字列を入れる

約103日前 2021年7月17日0:35
デジタル
Django

改訂履歴


2021/7/17 投稿

1. 背景


Djangoを使った開発中に覚えたことを備忘録として残します。

今回はモデルのCharFieldにランダムな文字列を自動で入力する方法を解説します。


2. ゴール


モデル生成時に自動でCharFieldの中身を「"適当な文字" + UUID」に設定する。


3. はじめに


みなさんUUIDは知っていますか?

分からない方は こちらの記事 で簡単に解説しておりますので、ご確認下さい。

ゴールにも書いたように、今回の目標はモデル生成時に自動でCharFieldの中身を「"適当な文字" + UUID」に設定することです。

なぜこんなことを行うのか簡単に背景を説明します。



この記事のようなWEBページを作る際、HTML言語を使って内容を記述しています。

HTML言語はタグと呼ばれる<>で囲まれたブロックで構成されており、そのタグの種類も色々あります。


タグを書くとき、タグの中に「id」や「class」といった情報を入力することが可能でして、そういった情報を付与することで便利に使うことができます。

その情報の中でも今回着目したいのが「id」です。


idというのはそのタグ自体が何かを示す「識別情報」のようなものです。

idには好きな文字列を入力することができるのですが、「先頭の文字に数字を使えない」という約束があります。
(ここ大事です)



私がアプリ開発を行う中で、どうしてもタグのidに「UUID」を使いたくなることがありました。

ですがUUIDはランダムな文字列ですので、「先頭に数字がきてしまう」ことがあります。

そこで考えた解決策が「UUID」を「"適当な文字" + UUID」に変更するという手段でした。


Djangoには元々「UUIDField」というものがあるのですが、今回作りたいのは「"適当な文字" + UUID」ですので使えません。

代わりにCharFieldの中でUUIDを用いる方法を採用しました。

次項で具体的な方法を解説します。



4. "適当な文字" + UUID


当初の方針はこうでした。

「CharFieldの中でUUIDを生成しつつ、先頭に文字を追加する」

コードで書くとこんな感じです。
id = models.CharField(default="s" + str(uuid.uuid4()))
default引数で「"s" + str(uuid.uuid4())」とすることで、インスタンス生成時に自動でidが付与されるようにしました。



ですが、実はこれ上手く動作しません。

エラーは何も出ないのですが、異常事態が起こります。

複数のモデルインスタンスを生成し「idフィールド」を確認してみると、全て同じidが付与されていました。


原因を調べてみるとどうも、models.pyで「idフィールド」が作られた瞬間にdefaultで1種類のidが生成されており、それが全てのモデルで共通のdefault idとして使われていたみたいです。



そこで下記のような対策を行いました。

①models.pyのidフィールド内でUUID関数を実行せず、”適当な文字”だけdefaultで入力する。

②モデルインスタンスの生成時に都度UUID関数を実行してランダムな文字列を取得。

③idフィールドにUUIDを追加し、モデルを保存する。



実際には下記のように書きました。
【models.py】
id = models.CharField(default="s")

【views.py】
"""HTMLからモデルを生成するPOSTリクエストを受け取ったとき"""
if request,method == "POST";
form = Form(request.POST)
if form.is_valid():
model = form.save(commit=False)
model.id += str(uuid.uuid4())
model.save()

model.idにはデフォルトで「s」という文字入っており、「+= str(uuid.uuid4())」でUUIDを追加しています。

その後、save()することで「"適当な文字" + UUID」というidフィールドを持ったモデルが生成されます。


create()メソッドを使うときでも同じことが可能です。
Model.objects.create(id = "s" + str(uuid.uuid4())
idフィールドの引数を渡すときにUUIDを生成しています。


本記事でお伝えしたかったのは、

「フィールドのdefault引数でランダムな文字列を生成しても意味ないよ」

ということです。



5. さいごに


同じようなことをしようとして上手くいっていない例がありました。

Django, randomization of “default” parameter of a model

How to set a random integer as the default value for a Django CharField?

出来なければ、出来る方法を考えると良いと思います!