Djangoで、Userモデルのカスタマイズ(OneToOneで、別モデルと紐づける

Twitterでシェア FaceBookでシェア はてなブックマークでシェア

Python - Django
2018年12月1日5:31に更新(約10日前)
2018年10月22日2:52に作成(約50日前)

旧ブログ移行記事です。

概要

今回はUserモデルをカスタマイズします。

DjangoのUserモデルは便利ですが、物足りなく感じることもあります。例えば、電話番号等や住所等の情報が欲しいときです。これらはUserモデルにはありません。

Userモデルを自前で定義する方法もありますが、今回はシンプルにUserモデルと紐づくProfileモデルを作成し、Userモデルにない情報を紐づけます。

models.py

プロフィール情報のモデルです。見たまんまですね。

from django.conf import settings
from django.db import models


GENDER_CHOICES = (
    ('1', '女性'),
    ('2', '男性'),
)


class Profile(models.Model):
    name = models.CharField("ハンドルネーム", max_length=255)
    phone = models.CharField("電話番号", max_length=255, blank=True)
    gendar = models.CharField("性別", max_length=2, choices=GENDER_CHOICES, blank=True)
    user = models.OneToOneField(settings.AUTH_USER_MODEL)

    def __str__(self):
        return self.name

ここでOneToOneFieldを指定しています。これにより、profile.useruser.profileとすることで、相互に呼び出すことができます。

user = models.OneToOneField(settings.AUTH_USER_MODEL)

forms.py

User用フォームと、Profile用フォームです。 UserCreationFormは、Userモデルを使ったユーザ登録の際に強い威力を発揮します。 ProfileモデルはただのModelFormです。fieldsにuserが含まれていないことに注意です。

from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import Profile


class UserCreateForm(UserCreationForm):
    pass


class ProfileForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = (
            "name", "gendar", "phone"
        )

views.py

from django.contrib.auth import login
from django.shortcuts import render, redirect
from .forms import ProfileForm, UserCreateForm


def index(request):
    """トップ画面!"""

    return render(request, 'testapp/index.html')


def regist_user(request):
    """ユーザ作成ページへ"""

    user_form = UserCreateForm(request.POST or None)
    profile_form = ProfileForm(request.POST or None)
    if request.method == "POST" and user_form.is_valid() and profile_form.is_valid():

        # Userモデルの処理。ログインできるようis_activeをTrueにし保存
        user = user_form.save(commit=False)
        user.is_active = True
        user.save()

        # Profileモデルの処理。↑のUserモデルと紐づけましょう。
        profile = profile_form.save(commit=False)
        profile.user = user
        profile.save()

        login(
            request, user, backend="django.contrib.auth.backends.ModelBackend")

        return redirect("testapp:index")

    context = {
        "user_form": user_form,
        "profile_form": profile_form,
    }
    return render(request, 'testapp/regist.html', context)

テンプレートには複数のフォームを渡すことができますし、それを受け取ることも可能です。

    user_form = UserCreateForm(request.POST or None)
    profile_form = ProfileForm(request.POST or None)

...
...
    context = {
        "user_form": user_form,
        "profile_form": profile_form,
    }
    return render(request, 'testapp/regist.html', context)

もしくは、Dango、インラインフォームセットを使うのようにインラインフォームを使うことも考えられます。

次の部分はUserモデルの作成です。 このcommit=Falseはデータベースに保存せず、保存する前のモデルオブジェクトを返します。 そして、user.is_active等の属性へのアクセスと保存が可能になります。

        # Userモデルの処理。ログインできるようis_activeをTrueにし保存
        user = user_form.save(commit=False)
        user.is_active = True
        user.save()

Profileモデルの作成です。 Userモデルと紐づけています。

        # Profileモデルの処理。↑のUserモデルと紐づけましょう。
        profile = profile_form.save(commit=False)
        profile.user = user
        profile.save()

そして、ログインさせています。authenticate関数を使わずにログインさせる場合はbackendの指定が必要です。login関数にbackendという引数を指定しています。指定しているのは、Djangoのデフォルトのバックエンドです。

        login(
            request, user, backend="django.contrib.auth.backends.ModelBackend")

base.html

{% load staticfiles %}
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
    {% block content %} {% endblock %}
</body>

</html>

index.html

templateでは、無条件で{{ user }}が使えます。 なので、それに紐づくprofileも使えます。

{% extends "testapp/base.html" %}
{% block content %}

<h1>
{% if user.is_authenticated %}
ユーザ名:{{ user.username }}<br>
ハンドルネーム:{{ user.profile.name }}<br>
性別:{{ user.profile.gendar }}<br>
電話:{{ user.profile.phone }}<br>
{% else %}
ようこそ、ゲスト
{% endif %}
</h1>
<hr>
<a href="{% url 'testapp:regist_user' %}">ユーザ作成</a>
{% endblock %}

register.html

2種類のフォームを、

タグの中に書きます。

{% extends "testapp/base.html" %}
{% block content %}
<form action="" method="POST">
{{ user_form.as_p }}
<hr>
{{ profile_form.as_p }}
{% csrf_token %}
<input type="submit" value="送信">
</form>
{% endblock %}

こうなると、admin管理画面でもUserとProfileを一緒に登録したくなるものです。 これはadmin.pyを以下のように変更するだけでできます。

from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
from .models import Profile


class ProfileInline(admin.StackedInline):
    model = Profile
    max_num = 1
    can_delete = False


class UserAdmin(AuthUserAdmin):
    inlines = [ProfileInline]


admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Twitterでシェア FaceBookでシェア はてなブックマークでシェア

記事にコメントする