このページでは、Django における「OneToOneField
を利用した User
の拡張」について解説をしていきます。
下記ページで解説しているとおり、OneToOneField
フィールドは、2つのモデルクラスのインスタンスを1対1で関連付けるためのリレーションフィールドになります。
この OneToOneField
を利用するメリットの1つは、既存のモデルクラスを変更することなく、そのモデルクラスから新たなフィールドを利用できるよう拡張できる点になります。
今回は、この既存のモデルクラスとして User
を扱い、この User
を変更することなく OneToOneField
で拡張する手順について説明していきます。
この User
は、Django に標準で用意されたユーザーを管理するモデルクラスであり、この User
を利用してユーザーのログイン等を実現することが可能です。このログインについては下記ページで解説していますので、興味があれば下記ページをご参照いただければと思います。
ただし、User
にはユーザーを管理する際の一般的なフィールドしか存在しませんので、開発対象のウェブアプリ特有のフィールドが必要になるのであれば、そのフィールドを別途追加する必要があります。ですが、User
は Django によって用意されている既に完成されたモデルクラスなので、User
を直接変更することは非推奨です。そのため、別の方法でフィールドの追加を行う必要があります。
その方法の1つが、今回紹介する OneToOneField
を利用した User
の拡張になります。具体的には、User
に追加したいフィールドを持つモデルクラスを定義し、そのモデルクラスのインスタンスと User
とを関連付ける方法になります。これにより、User
を変更することなく、User
に擬似的にフィールドを追加することが可能となります。
このような、OneToOneField
を利用した User
の拡張手順について、このページで解説を行なっていきます。
ちなみに、ユーザーを管理するモデルクラスにフィールドを追加する方法としては、ユーザーを管理するモデルクラスとして別途 カスタムユーザー
を定義し、その カスタムユーザー
を開発対象のウェブアプリに応じてカスタマイズする方法も挙げられます。
これに関しては、下記ページで解説していますので、詳しく知りたい方は下記ページをご参照いただければと思います。
【Django入門6】モデルの基本Contents
OneToOneField
を利用した User
の拡張
では、OneToOneField
を利用した User
の拡張手順について解説していきます。
OneToOneField
を利用した User
の拡張手順
OneToOneField
は Django のリレーションフィールドの1つであり、この OneToOneField
のフィールドをモデルクラスに定義することで、他のモデルクラスとの1対1の関連付けを実施することが可能となります。
このリレーションについては別途下記ページで解説していますので、リレーションについての詳細に関しては下記ページを参照していただければと思います。
【Django入門7】リレーションの基本この OneToOneField
を利用すれば、既存のモデルクラスを変更することなく、その既存のモデルクラスに擬似的に任意のフィールドを追加して拡張することが可能となります。
OneToOneField
を利用して User
の拡張を行う具体的な手順は下記のようになります。
User
に追加したいフィールドを持つモデルクラスを新たに定義する- 定義したモデルクラスに、第1引数を
User
とするOneToOneField
を定義する - 定義したモデルクラスのインスタンスと
User
のインスタンスを関連づける
まず、User
に追加したいフィールドを持つモデルを models.py
に新たに定義します。
次に、定義したモデルクラスに User
を第1引数とする OneToOneField
のフィールドを定義します。これにより、定義したモデルクラスのインスタンスと User
のインスタンスが関連付け可能な状態となります。
また、リレーションフィールドを定義すれば、その定義先のモデルクラスには、その フィールド名
のデータ属性が追加されることになり、さらに、そのリレーションフィールドの第1引数に指定されたモデルクラスには、相手のモデルクラス名
のデータ属性が追加されることになります。
これらの追加されたデータ属性を利用することで、定義したモデルクラスのインスタンスと User
のインスタンスを関連付けることが可能となります。例えば、User
のインスタンスのデータ属性 相手のモデルクラス名
に定義したモデルクラスのインスタンスを参照させることで、これらのインスタンス同士が関連付けられることになります。
このように、2つのインスタンスを関連付けておけば、追加されたデータ属性を介して一方のインスタンスから他方のインスタンスのフィールドに値をセットしたり、フィールドから値を取得したりすることができるようになります。そのため、User
の変更は行なっていないにも関わらず、User
から他方のインスタンスのフィールドを利用することで、あたかも User
にフィールドが追加されたように User
のインスタンスを扱うことが可能となります。
以上が、OneToOneField
を利用して User
の拡張を行う手順の説明となります。
スポンサーリンク
OneToOneField
で拡張した User
の利用例
次は、OneToOneField
を利用した User
の拡張の利用例として、ユーザーの名前・パスワード・身長・体重を管理するウェブアプリの開発例を示していきたいと思います。
簡単な例となりますが、OneToOneField
を利用した User
の拡張の仕方や、拡張した User
の使い方のポイントは掴めると思いますので、是非ここからの解説も読み進めていただければと思います。
プロジェクトとアプリの作成
最初に、プロジェクトとアプリの作成を行なっていきます。この辺りはいつも通りの手順で実施すれば良いです。
具体的には、まず適当なフォルダに移動した後、下記のコマンドを実行して test_project
を生成します(念のため言っておきますが、コマンドの先頭の %
に関しては入力不要です)。
% django-admin startproject test_project
これにより、test_project
というフォルダが生成されるので、このフォルダに移動を行い、続いて下記のコマンドを実行して test_app
を作成します。
% python manage.py startapp test_app
以上でプロジェクトとアプリが作成できたことになります。
ただし、現状ではプロジェクトにアプリがまだ認識されていないため、test_project/settings.py
内の INSTALLED_APPS
を下記のように変更してアプリの登録を行います。これにより、test_app
がプロジェクトに登録されることになります。
INSTALLED_APPS = [
'test_app', # 追加
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
モデルクラスを定義する
続いて、test_app/models.py
を変更してモデルクラスの定義を行なっていきます。
追加したいフィールドを持つモデルクラスの定義
まずは、User
に追加したいフィールドを持つモデルクラスの定義を行なっていきます。
今回は、ユーザーの名前・パスワード・身長・体重を管理するアプリを開発していくわけですが、User
には身長や体重を管理するフィールドは存在しません。そのため、新たにモデルクラスを定義し、そのモデルクラスに身長と体重を管理するフィールドを持たせるようにしていきます。具体的には、身長(cm)を示すフィールド height
、体重(kg)を示すフィールド weight
を持つ Profile
というモデルクラスを定義していきたいと思います。
これは、test_app/models.py
を下記のように変更することで実現できます。
from django.db import models
class Profile(models.Model):
height = models.FloatField()
weight = models.FloatField()
リレーションフィールドの定義
続いて、Profile
に第1引数を User
とするリレーションフィールドを定義し、Profile
のインスタンスと User
のインスタンスとを関連付けできるようにしていきます。
リレーションフィールドとしては OneToOneField
を利用します。これにより、リレーションフィールドを定義することで Profile
のインスタンスと User
のインスタンスとは1対1の関連付けが行えるようになります。
このために、test_app/models.py
を下記のように変更します。
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
height = models.FloatField()
weight = models.FloatField()
user = models.OneToOneField(User, on_delete=models.CASCADE)
このようにリレーションフィールドを定義すれば、User
のインスタンスには profile
というデータ属性が、Profile
のインスタンスには user
というデータ属性が追加され、これらのデータ属性を利用することで、インスタンスの関連付けであったり、関連したインスタンスの取得が行えるようになります。
この辺りの、リレーションに関する一般的な解説は下記ページで説明していますので、詳細に関しては下記ページを参照してください。
【Django入門7】リレーションの基本また、OneToOneField
には on_delete
引数の指定が必須となります。この on_delete
引数に関しては下記ページで解説していますので、詳細に関しては下記ページを参照してください。
以上で、モデルクラスの定義は完了となります。
スポンサーリンク
フォームクラスを定義する
今回は、ユーザーの登録をフォームから実施できるようにしていきたいと思います。
フォームの表示方法等、フォームの扱い方等に関しては下記ページで解説していますので、詳しくは下記ページを参照していただければと思います。
【Django入門5】フォームの基本定義するフォームクラス
フォームでは下記の入力フィールドを表示してユーザーが各種データを入力できるようにしていきたいと思います。括弧内は、その情報を扱うモデルクラスのフィールド名を表しており、username
と password
に関しては User
で、height
と weight
に関しては Profile
に定義されたフィールドとなります。
- 名前(
username
) - パスワード(
password
) - 身長(
height
) - 体重(
weight
)
ちなみに、今回はパスワードは不要なのですが、User
で password
が必須フィールドととして定義されているため、フォームから入力受付を行うようにしたいと思います。ちなみに、ログイン機能等を実現する時には password
の入力受付は必須となります。
また、今回は、単なるフォームクラスではなく、モデルフォームクラスを定義してフォームを扱うようにしていきたいと思います。下記ページで解説しているとおり、モデルフォームクラスとは ModelForm
を継承するクラスであり、モデルフォームクラスでは model
属性で指定したモデルクラスをベースとしたフォームを定義することができます。具体的には、model
属性で指定したモデルクラスに定義されたフィールドと同じフィールドを持つフォームを定義することができます。
ただし、今回は User
と Profile
の2つのモデルクラスを扱うため、モデルフォームクラスも2つ定義することになります。具体的には、User
をベースとするモデルフォームクラスには username
と password
の2つの入力フィールドを持たせ、Profile
をベースとするモデルフォームクラスには height
と weight
の2つの入力フィールドを持たせるようにしていきます。
さらに、User
等のユーザー管理モデルクラスをベースとしてモデルフォームクラスを定義する場合は、ModelForm
ではなく UserCreationForm
を継承させる方が良いので(パスワードが自動的に暗号化される)、今回も User
をベースとするモデルフォームクラスは UserCreationForm
を継承して定義していきたいと思います。UserCreationForm
もモデルフォームクラスの一種となるため、UserCreationForm
を継承して定義したフォームもモデルフォームクラスと同様にして扱うことが可能です。
少し前置きが長くなりましたが、次は実際に上記のような2つのモデルフォームクラスを定義していきましょう。
forms.py
まずは、test_app
フォルダの中に forms.py
という名前のファイルを新規作成してください。そして、forms.py
の中身を下記のように変更すれば、上記のような2つのモデルフォームクラスを定義することができます。
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class UserForm(UserCreationForm):
class Meta:
model = User
fields = ['username']
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['height', 'weight']
モデルフォームクラスでは、model
属性に指定したモデルクラスに定義されたフィールドのうち、fields
で指定したフィールドのみを持つフォームが定義されることになります。なので、ProfileForm
のインスタンスを出力すれば、height
と weight
の入力フィールドが表示されることになります。
UserForm
では fields
に 'username'
しか指定していないので、username
の入力フィールドしか表示されないようにも思えますが、UserCreationForm
を継承すれば password
の入力フィールドは自動的に追加されるようになっているため、UserForm
のインスタンスを出力すれば username
と password
の入力フィールド(確認用パスワードの入力フィールドを含む)が表示されることになります。さらに、password
の入力フィールドへの入力文字列は伏せ字で表示されることになりますし、password
の入力フィールドに入力されたパスワードはデータベース保存時に暗号化された状態で保存されることになります。この辺りは UserCreationForm
を継承すれば自動的に UserCreationForm
が行ってくれます。
2つのフォームの扱い方
ただし、上記のようにフォームクラスを定義した場合、username
と password
の入力フィールドを表示するフォームと height
と weight
の入力フィールドを表示するフォームとが別々に作成されることになります。そのため、これら4つの入力フィールドを一緒に表示するためには、テンプレートファイルから、これらのフォームを一緒に出力する必要があります(確認用パスワードの入力フィールドも含めると5つの入力フィールドを含むフォームが出力されることになります)。
そのため、こういった2つのモデルフォームクラスのインスタンスを一緒に出力できるよう、テンプレートファイルやビューを作成していく必要があることになります。
ビューとテンプレートファイルを作成する
ここからは、ビューをとテンプレートファイルを作成していきたいと思います!
今回は、ウェブアプリで2つのページを表示できるようにしていきたいと思います。1つがユーザー登録フォームを表示するページで、もう1つが登録済みのユーザー一覧を表示するページになります。そのため、ここからは、これらのページを表示するためのビューとテンプレートファイルを作成していくことになります。
ユーザー登録フォームを表示するテンプレートファイル
最初に、ユーザー登録フォームを表示するページのテンプレートファイルを作成していきましょう!
まずは、テンプレートファイルの置き場所として test_app
フォルダの下に templates
フォルダを作成し、さらに templates
フォルダの下に test_app
フォルダを作成してください。
そして、最後に作成した test_app
フォルダの中に register.html
を新規作成し、そのファイルの中身を下記のように変更してください。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<main class="container my-5 bg-light">
<h1>ユーザー登録</h1>
<form action="{% url 'register' %}" method="post">
{% csrf_token %}
<table class="table table-hover">
<tbody>
{% for form in forms %}
{{ form }}
{% endfor %}
</tbody>
</table>
<input type="submit" class="btn btn-primary" value="送信">
</form>
</main>
</body>
スタイルシートの読み込みなどを行っているので少し複雑なようにも感じるかもしれませんが、このテンプレートファイルでポイントになるのは下記の部分のみになります。
{% for form in forms %}
{{ form }}
{% endfor %}
前述の通り、今回は定義した2つのフォームクラスを一緒に出力して1つのフォームとして表示するようにしていきます。これは、ビューから、それらの2つのフォームのインスタンスを要素とするリストを受け取り、そのリストに含まれるインスタンスを全て出力することで実現することができます。
そのため、上記のコードのように、リスト(上記の場合は forms
)に対して for
ループを実施し、さらにそのリストに含まれるインスタンス(上記の場合は form
)を1つ1つ出力するようにすれば、複数のフォームの出力が実現できることになります。
ただし、フォームのインスタンスのリストを register.html
が受け取れるよう、ビューは、フォームのインスタンスのリストがセットされたコンテキストを作成する必要があります。
ユーザー登録フォームを扱うビュー
ということで、その点を考慮しながら、次はビュー側を作成していきます。
このビューはフォームを扱うビューとなりますので、基本的には下記ページの ビューでフォームクラスを扱う で説明している通りに実装すれば良いだけになります。
【Django入門5】フォームの基本ただし、先ほど説明したように、コンテキストには2つのフォームのインスタンス、具体的には UserForm
のインスタンスと ProfileForm
のインスタンスを要素とするリストを 'forms'
キーの値としてセットしておく必要があります。
また、表示されるフォームでは UserForm
のフィールドと ProfileForm
のフィールドの両方の値が入力されるわけですから、その送信されてくるデータには UserForm
のフィールドの値と ProfileForm
のフィールドの値が含まれることになります。そういったデータから、UserForm
のインスタンスと ProfileForm
のインスタンスを生成して妥当性の検証を行う必要がありますし、これらのモデルフォームクラスのインスタンスから、ベースとなるモデルクラスのインスタンス、具体的には User
と Profile
のインスタンスを取得し、さらには、それらのインスタンスの関連付けも必要となります。
これらの全てを満たす、ユーザー登録フォームを扱うビューは、下記のような register_view
関数となります。この register_view
は、test_app/views.py
に定義する必要があります。
from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from .forms import UserForm, ProfileForm
def register_view(request):
if request.method == 'POST':
# 受信データからUserFormのインスタンス生成
user_form = UserForm(request.POST)
# 受信データからProfileFormのインスタンス生成
profile_form = ProfileForm(request.POST)
forms = [user_form, profile_form]
if user_form.is_valid() and profile_form.is_valid():
# 受信データが妥当な場合
# フォームの各種フィールドに入力された値から
# UserとProfileのインスタンス生成
user = user_form.instance
profile = profile_form.instance
# インスタンスを関連付け
user.profile = profile
# インスタンスをレコードとして保存
user.save()
profile.save()
# ユーザー一覧ページに遷移
return redirect('users')
else:
# フィールドが空のUserFormのインスタンス生成
user_form = UserForm(request.POST)
# フィールドが空のProfileFormのインスタンス生成
profile_form = ProfileForm(request.POST)
forms = [user_form, profile_form]
# 2つのフォームのインスタンスのリストをコンテキストにセット
context = {
'forms': forms
}
return render(request, 'test_app/register.html', context)
前述の通り、この register_view
の基本的な処理の流れは、下記ページの ビューでフォームクラスを扱う で説明した内容の通りになっています。
ただし、これも前述の通り、コンテキストの値としてリストをセットする点や、インスタンス同士の関連付けが必要となる点は注意が必要となります。
前者に関しては、forms
変数にリストを参照させるあたりの処理を見ていただければ、どういった処理が必要であるかについて理解していただけると思いますので、説明を省略します。
後者に関しても、request.method == 'POST'
が成立した時の処理を確認していただければ、どういった処理を実行すれば実現できるのかを理解していただけるのではないかと思います。が、念の為、ポイントを3点説明しておきます。
ポイントの1つ目は、下記のフォームのインスタンスを生成している箇所となります。
# 受信データからUserFormのインスタンス生成
user_form = UserForm(request.POST)
# 受信データからProfileFormのインスタンス生成
profile_form = ProfileForm(request.POST)
今回、1つのフォームに UserForm
のフィールドと ProfileForm
のフィールドの両方を表示するようになっていますので、フォームのボタンクリック時にウェブアプリに送信されてくるデータにも、UserForm
のフィールドに入力された値と ProfileForm
のフィールドに入力された値の両方が含まれることになります。
このようなデータであっても、request.POST
を引数として UserForm()
を実行すれば UserForm
のフィールドに入力された値を反映した UserForm
のインスタンスが、request.POST
を引数として ProfileForm()
を実行すれば ProfileForm
のフィールドに入力された値を反映した ProfileForm
のインスタンスをそれぞれ生成することが可能です。
なので、もちろん実行するコンストラクタの数は増えることになりますが、1つのフォームに複数のフォームクラスのフィールドを含ませて表示させたとしても、基本的には1つのフォームクラスのフィールドから構成されるフォームと同様の手順でフォームクラスのインスタンスは生成可能です。ただし、フォームから受信したデータを扱うためには妥当性の検証を実施する必要がありますので、複数のフォームクラスのインスタンスを生成するのであれば、それら全てに対して is_valid
メソッドを実施させる必要があることに注意してください。
ポイントの2つ目は、モデルフォームクラスのインスタンスからの、そのベースとなるモデルクラスのインスタンスの取得方法になります。is_valid
メソッドを実行して妥当性の検証結果が OK と判断されたモデルフォームクラスのインスタンスからは、instance
データ属性より、そのモデルフォームクラスのベースとして指定されたモデルクラスのインスタンスが取得可能となります。このインスタンスの各種フィールドには、モデルフォームクラスのインスタンスのフィールドにセットされている値がそのまま反映されていることになります。
このように、フォームのインスタンスからモデルクラスのインスタンスを容易に取得可能であるという点もモデルフォームクラスの特徴の1つとなります。そして、この手順で User
のインスタンスと Profile
のインスタンスを取得しているのが下記部分となります。
# フォームの各種フィールドに入力された値から
# UserとProfileのインスタンス生成
user = user_form.instance
profile = profile_form.instance
3つ目のポイントが、上記で取得した User
のインスタンスと Profile
のインスタンスとの関連付けになります。この Profile
は、あくまでも User
にフィールドを追加するために定義したモデルクラスなので、User
のインスタンスと Profile
のインスタンスを関連付けないと意味がありません。また、モデルクラスを定義する で説明したように、Profile
にリレーションフィールドを定義することで、User
のインスタンスにはデータ属性 profile
が追加されることになります。そして、そのリレーションフィールドが OneToOneField
の場合、リレーションフィールドの定義によって追加されたデータ属性で相手を参照させることで、参照元と参照先のインスタンスを関連付けることができます。
で、この関連付けと、その関連付けしたインスタンスのデータベースへの保存を行なっているのが下記部分となります。
# インスタンスを関連付け
user.profile = profile
# インスタンスをレコードとして保存
user.save()
profile.save()
地味ですが、上記の user.save()
と profile.save()
の実行順序も重要となります。これらの実行順序が逆になると例外が発生し、データベースへの保存に失敗することになります。この理由は、下記ページの プライマリーキー確定後に関連付けを行う必要がある で解説しているので、詳細を知りたい方は下記ページを参照していただければと思います。
ユーザー一覧を表示するテンプレートファイル
続いて、ユーザー一覧を表示するページを実現していきましょう!具体的には、データベースに保存されている全ユーザーの名前・身長・体重の一覧を表示するページを実現していきたいと思います。
ポイントは、身長・体重の表示になります。これらの身長・体重を扱うフィールドを User
のインスタンスは持っていません。ですが、User
のインスタンスに関連付けられた Profile
のインスタンスが height
フィールドと weight
フィールドを持っていますので、User
のインスタンスから、関連付けられた Profile
のインスタンスの height
フィールドと weight
フィールドの値を取得してやれば、ユーザーの名前に合わせて身長・体重も表示することができることになります。
また、関連付けられたインスタンスのフィールドの値の取得は、関連付けを行うリレーションフィールドが OneToOneField
である場合、リレーションフィールドの定義によって追加されたデータ属性を利用し、インスタンス.データ属性.フィールド名
によって取得可能となります。
具体的には、User
のインスタンスを user
とすれば、user.profile.height
で身長の値、user.profile.weight
で体重の値が取得できることになります。
なので、テンプレートファイルは、全 User
のインスタンスを要素とするリストさえビューから受け取れば、その User
から取得した1つ1つのインスタンスに設定された名前・身長・体重を出力することができることになります。で、このような出力を行うテンプレートファイルが下記となります。このファイルは、前述でテンプレートファイルの置き場として作成した test_app
フォルダの中に、users.html
という名前で保存してください。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<main class="container my-5 bg-light">
<h1>ユーザーの身長と体重</h1>
<table class="table table-hover">
<tbody>
<tr>
<th>名前</th>
<th>身長</th>
<th>体重</th>
</tr>
{% for user in users %}
<tr>
<td>{{ user.username }}</td>
<td>{{ user.profile.height }}</td>
<td>{{ user.profile.weight }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</main>
</body>
実際に、各ユーザーの名前・身長・体重の出力を行なっている箇所は下記となります。User
を OneToOneField
を利用して拡張する場合、User
のインスタンスと他のモデルクラスのインスタンスとの関連付けも必要ですし、何らかの機能を実現するためには、下記のように User
のインスタンスに関連付けられた相手のフィールドの値の取得も必要となります。
{% for user in users %}
<tr>
<td>{{ user.username }}</td>
<td>{{ user.profile.height }}</td>
<td>{{ user.profile.weight }}</td>
</tr>
{% endfor %}
ユーザー一覧を表示するビュー
次は、ユーザー一覧を表示するビューの定義を行います。このビューに関しては、一般的なインスタンスの一覧の表示と同様の実装で実現できます。
具体的には、下記のように views.py
を変更して users_view
を定義してやれば良いだけになります。
from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from .forms import UserForm, ProfileForm
def register_view(request):
if request.method == 'POST':
# 受信データからUserFormのインスタンス生成
user_form = UserForm(request.POST)
# 受信データからProfileFormのインスタンス生成
profile_form = ProfileForm(request.POST)
forms = [user_form, profile_form]
if user_form.is_valid() and profile_form.is_valid():
# 受信データが妥当な場合
# フォームの各種フィールドに入力された値から
# UserとProfileのインスタンス生成
user = user_form.instance
profile = profile_form.instance
# インスタンスを関連付け
user.profile = profile
# インスタンスをレコードとして保存
user.save()
profile.save()
# ユーザー一覧ページに遷移
return redirect('users')
else:
# フィールドが空のUserFormのインスタンス生成
user_form = UserForm(request.POST)
# フィールドが空のProfileFormのインスタンス生成
profile_form = ProfileForm(request.POST)
forms = [user_form, profile_form]
# 2つのフォームのインスタンスのリストをコンテキストにセット
context = {
'forms': forms
}
return render(request, 'test_app/register.html', context)
def users_view(request):
# Userのインスタンスを全て取得
users = User.objects.all()
# Userのインスタンスのリストをコンテキストにセット
context = {
'users': users
}
return render(request, 'test_app/users.html', context)
URL とビューの関数のマッピング
最後に、ここまで作成してきたビューの関数が、クライアントからのリクエストに応じて実行されるよう、urls.py
で URL とビューの関数とのマッピングを行なっていきます。
まず、test_project/urls.py
を下記のように変更します。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('test/', include('test_app.urls'))
]
続いて、test_app/urls.py
を新規に作成し、中身を下記のように変更します。
from django.urls import path
from . import views
urlpatterns = [
path('register/', views.register_view, name='register'),
path('users/', views.users_view, name='users')
]
これにより、URL が /test/register/
のリクエストを受け取った時には register_view
が実行されてユーザー登録フォームが表示され、さらに URL が /test/users/
のリクエストを受け取った時には users_view
が実行されてユーザー一覧が表示されるようになります。
以上で、ユーザーの名前・パスワード・体重・身長を管理するウェブアプリの完成となります。ポイントは、今回は User
を一切変更することなく、ユーザーの体重・身長を管理を実現したという点になります。このようにOneToOneField
を利用することで、既存のモデルクラスを変更することなく、擬似的な拡張を行うことが可能となります。
既存のモデルクラスを変更できない・変更したくないような場面では、この OneToOneField
の利用が有効ですので、是非 OneToOneField
での既存のモデルクラスの拡張の仕方については覚えておいてください!
スポンサーリンク
まとめ
このページでは、Django における「OneToOneField
を利用した User
の拡張」について解説しました。
OneToOneField
を利用することで、2つのモデルクラスのインスタンス同士を1対1で関連付けすることができるようになります。そして、これを利用することで、User
を変更しなくても User
から他のモデルクラスのフィールドを参照することができ、User
から新たなフィールドを扱うことができるようになります。
今回は User
を拡張することを目的とした解説になりましたが、もちろん他のモデルクラスを変更することなく拡張したいような場合にも利用できます。品質などを考慮すると、既存のモデルクラスは変更せず、そのまま利用したくなることも多いです。そんな時に、この OneToOneField
を利用した拡張方法が有効となりますので、この拡張方法については是非覚えておきましょう!