【Django 1.8】refresh_from_dbを使ってモデルをリロードする

DJango1.8からModelクラスにrefresh_from_dbというメソッドが追加されました。このメソッドは名前の通り、モデルの情報をDBからリロードします。

個人的には、特にテストケースでの利用に便利だと感じました。

テスト対象

ユーザのメールアドレスを変更するViewがあるとします。このメールアドレスを変更するViewを使って、refresh_from_dbメソッドの使い道を見ていきます。

views.py

# coding: utf-8
from django.views.generic import View
from django.http import JsonResponse


class EmailUpdate(View):
    def post(self, request, *args, **kwds):
        """
        メールアドレスを更新します。
        ※説明用のViewなので、メールアドレスのValidationは未実装です。
        """
        email = request.POST.get('email')
        res = {'is_success': False}
        if request.user.is_authenticated():
            # メールアドレスを更新します。
            request.user.email = email
            request.user.save()
            res['is_success'] = True
        return JsonResponse(res)

urls.py

# coding: utf-8
from django.conf.urls import url
from myapp import views as myapp_views

urlpatterns = [
    url(r'^emailupdate/$', myapp_views.EmailUpdate.as_view(),
        name='myapp_emailupdate'),
]

refresh_from_dbの使い道

以下の様なテストケースがあったとします。

# coding: utf-8
import json
from django.contrib.auth.models import User
from django.test import TestCase, client
from django.core.urlresolvers import reverse


class TestEmailUpdate(TestCase):
    def setUp(self):
        super().setUp()
        self.client = client.Client()
        self.username = 'USERNAME'
        self.password = 'PASSWORD'
        self.email = 'foo@example.com'
        self.newemail = 'bar@example.com'
        self.user = User.objects.create_user(
            username=self.username, password=self.password, email=self.email)

    def test_django17(self):
        # ログインします。
        is_loggedin = self.client.login(
            username=self.username, password=self.password)
        self.assertTrue(is_loggedin)

        # メールアドレス変更のリクエストを投げます。
        res = self.client.post(
            reverse('myapp_emailupdate'), {'email': self.newemail})

        # レスポンスを確認します。
        self.assertEqual(res.status_code, 200)
        data = json.loads(res.content.decode('utf-8'))
        self.assertTrue(data['is_success'])

        # 以降、メールアドレスが変更されていることを確認します。

このテストケースでは最後の行以降に、メールアドレスが変更されたことを確認していきます。

refresh_from_dbを使わない場合

上記テストケースでメールアドレスの変更を確認していくとき、「self.user.email」は更新前の状態になっています。なぜならば、メールアドレスが更新されたのはサーバサイドの話で、クライアントサイドのモデルのインスタンスには影響を及ぼさないからです。

        # self.userのメールアドレスは変更されていません。
        self.assertEqual(self.user.email, self.email)
        self.assertNotEqual(self.user.email, self.newemail)

そのため、メールアドレスの更新を確認するためには、DBから最新の情報を取ってくる必要があります。

        # DBからユーザ情報を再取得します。
        self.user = User.objects.get(pk=self.user.pk)

最新の情報をとってきた後、メールアドレスの変更を確認することができます。

        # self.userのメールアドレスは変更されています。
        self.assertEqual(self.user.email, self.newemail)
        self.assertNotEqual(self.user.email, self.email)

refresh_from_dbを使う場合

refresh_from_dbを使うと、DBから最新情報を取得する部分(self.user = User.objects.get(pk=self.user.pk))を書き換えることができます。

        # DBからユーザ情報を再取得します。
        # self.user = User.objects.get(pk=self.user.pk)
        self.user.refresh_from_db()

大きな修正ではないようにも見えますが、必要に応じてモデルクラスのインポートなどを考えなくていいので、個人的には便利なメソッドだと感じました。

テストケースのソース全体

以下に、今回使ったテストケースのソース全体を載せます。

# coding: utf-8
import json
from django.contrib.auth.models import User
from django.test import TestCase, client
from django.core.urlresolvers import reverse


class TestEmailUpdate(TestCase):
    def setUp(self):
        super().setUp()
        self.client = client.Client()
        self.username = 'USERNAME'
        self.password = 'PASSWORD'
        self.email = 'foo@example.com'
        self.newemail = 'bar@example.com'
        self.user = User.objects.create_user(
            username=self.username, password=self.password, email=self.email)

    def test_django17(self):
        # ログインします。
        is_loggedin = self.client.login(
            username=self.username, password=self.password)
        self.assertTrue(is_loggedin)

        # メールアドレス変更のリクエストを投げます。
        res = self.client.post(
            reverse('myapp_emailupdate'), {'email': self.newemail})

        # レスポンスを確認します。
        self.assertEqual(res.status_code, 200)
        data = json.loads(res.content.decode('utf-8'))
        self.assertTrue(data['is_success'])

        # 以降、メールアドレスが変更されていることを確認します。

        # self.userのメールアドレスは変更されていません。
        self.assertEqual(self.user.email, self.email)
        self.assertNotEqual(self.user.email, self.newemail)

        # DBからユーザ情報を再取得します。
        self.user = User.objects.get(pk=self.user.pk)

        # self.userのメールアドレスは変更されています。
        self.assertEqual(self.user.email, self.newemail)
        self.assertNotEqual(self.user.email, self.email)

    def test_django18(self):
        # ログインします。
        is_loggedin = self.client.login(
            username=self.username, password=self.password)
        self.assertTrue(is_loggedin)

        # メールアドレス変更のリクエストを投げます。
        res = self.client.post(
            reverse('myapp_emailupdate'), {'email': self.newemail})

        # レスポンスを確認します。
        self.assertEqual(res.status_code, 200)
        data = json.loads(res.content.decode('utf-8'))
        self.assertTrue(data['is_success'])

        # 以降、メールアドレスが変更されていることを確認します。

        # self.userのメールアドレスは変更されていません。
        self.assertEqual(self.user.email, self.email)
        self.assertNotEqual(self.user.email, self.newemail)

        # DBからユーザ情報を再取得します。
        # self.user = User.objects.get(pk=self.user.pk)
        self.user.refresh_from_db()

        # self.userのメールアドレスは変更されています。
        self.assertEqual(self.user.email, self.newemail)
        self.assertNotEqual(self.user.email, self.email)

この記事が役に立った場合、シェアしていただけると励みになります!!