Django設計 モデルクラスにカスタムメソッドを定義する

Djangoのモデルクラスにはカスタムメソッドを定義できます。

テーブルの”行レベルの機能”を追加したい時はモデルクラスに定義するのが適しています。

今回はカスタムメソッドを定義するメリットを解説します。

サンプル

「ログインユーザがアクセスできるマイページに、ログインユーザのフルネームを表示する機能」を追加する方法を以下の2つの方法で実装してみます。

  • ビュー関数に実装する方法
  • モデルクラスにカスタムメソッドを定義する方法

ユーザーモデルは以下の様にAbstractUserを継承して作成したユーザーモデルを使用します。

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    pass

ビュー関数に実装する方法

ビュー関数に実装すると以下のようになります。(仕様追加に影響する部分のみを抜粋しています)

from django.shortcuts import render

def mypage(request):
    """マイページのビュー関数"""
    context = {}
    user = request.user
    full_name =  f'{user.last_name} {user.first_name}'.strip()
    context['full_name'] = full_name
    return render(request, 'accounts/mypage.html', context)

request.userはログインユーザーのインスタンスです。上記で定義したUserモデルのインスタンスでもあります。

このモデルにはfirst_nameフィールドとlast_nameフィールドが定義されています。

この2つのデータを使ってfull_nameを定義し、contextに渡しています。テンプレート側で

{{ full_name }}

と書けば、フルネームが表示されます。

問題点

mypage関数にフルネームを取得するロジックを書いています。

ここではmypage関数は、userにfirst_nameフィールドとlast_nameフィールドがある事や、この2つのフィールドを使ってフルネームを作成する方法を知っている状態になります。

mypage関数はuserからフルネームを取得できれば良く、userのフィールド情報やフルネームの作成方法を知る必要はないのですが、知ってしまっている点が問題です。

モデルクラスにカスタムメソッドを定義する方法

フルネームはUserクラスのフィールドを使って作成しているためUserクラスにfull_nameを取得するメソッドを定義することが可能です。

フルネームを取得するメソッドをUserクラスに追加すると以下の様になります。

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    def get_full_name(self):
        """日本人の名前を想定 氏 名の順で表示"""
        full_name =  f'{user.last_name} {user.first_name}'
        return full_name.strip()

”フルネームを取得するロジックをmypage関数よりUserクラスに定義した方が適切だ”と理解できると思います。

ビュー関数は以下のようになります。

def mypage(request):
    '''マイページのビュー関数'''
    context = {}
    user = request.user   
    context['full_name'] = user.get_full_name()
    return render(request, 'accounts/mypage.html', context)

テンプレート側は「ビュー関数に実装する方法」と同じです。

メリット

mypage関数にフルネームを取得するロジックを書く必要がなくなり、get_full_nameメソッドを実行するだけでフルネームを取得できる様になりました。

「ビュー関数に実装する方法」のmypage関数と比較すると、userにfirst_nameフィールドやlast_nameフィールドがある事や、フルネームの実装方法を知る必要がなくなっています。

システム設計で

「機能は知っているが機能の詳細(ロジック)は知らない」

という考えがあります。今回の場合は

「mypage関数はuserからフルネームを取得できる機能(get_full_nameメソッド)は知っているが機能の詳細は知らない」

と言い換える事ができます。

他には以下のメリットがあります。

  • 他のビュー関数でフルネームが必要になったとしてもget_full_nameメソッドを呼ぶだけで解決する
  • get_full_nameメソッドのロジックが変わったとしてもビュー関数には影響がない
  • フルネームを取得するテストが実装しやすい

補足

補足 その1

今回のサンプルは解説のためにビュー側でcontext[‘full_name’]を定義しましたが、定義しなくても実装可能です。

テンプレートでrequest変数が使えるので、テンプレート側で{{ request.user.get_full_name }}と書けばフルネームを表示できます。この方法の方が簡潔です。

補足 その2

AbstractUserクラスにはget_full_nameメソッドが定義されていますがこれは、first_name, last_nameの順で表示されます。

今回は日本人の氏名の想定なのでこのメソッドをオーバーライドし、last_name, first_nameの順で表示する様にしました。

AbstractUserには他にもクラスメソッドが定義されています。クラスメソッドの参考になるので一度チェックしてみると良いでしょう。

まとめ

今回の例は単純ですが重要な考え方です。

今回の「モデルクラスにカスタムメソッドを定義する方法」ができる様になるためには

「フルネームのロジックはどこに書くのが一番ふさわしいか?」という観点で考える力や、

「行レベルの機能はモデルクラスに実装した方が良い」という知識が必要になります。

どちらも設計力を上げるために欠かせない要素です。

「モデルクラスにカスタムメソッドを定義する方法」を活用してより良い設計を目指しましょう。

スポンサーリンク
スポンサーリンク

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です