(最終更新月:2021年10月)
前回の記事では、SlugFieldを追加し、「pk」ではなく「slug」で特定のページへアクセスする方法について解説しました
当記事では、
- スラッグフィールドのデフォルト値を関数で設定する方法
について解説していきます
✔YouTube解説動画
当記事の内容は動画を見ながら進めると、約15分程度で完了します。
動画ならではの情報も解説しているので、記事と一緒にご覧ください。
動画の概要欄には、単元ごとのコードを紹介しているgithubページも載せています。
デフォルト値の設定
ランダムな文字列がデフォルト値となるよう設定していきます
まずはランダムな文字列を作り出す関数を別ファイルで定義していきます
ランダムな値を返す関数
utilsフォルダに新たなファイル「random_string.py」を作成し、必要な関数をこちらに定義していきます
utils > random_string.py
import random,string
def random_string_generator(size=20, chars=string.ascii_lowercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
こちらのrandom_string_generatorを使った関数にて、SlugFieldにデフォルト値を割り当てます
自動生成したスラッグをデフォルト値に設定
nippo > models.py
from utils.random_string import random_string_generator
#...他のインポートは割愛します
def slug_maker():
repeat = True
while repeat:
new_slug = random_string_generator()
counter = NippoModel.objects.filter(slug=new_slug).count()
if counter == 0:
repeat = False
return new_slug
#<<モデルマネジャー、クエリセットは割愛>>
class NippoModel(models.Model):
#...,
slug = models.SlugField(max_length=20, unique=True, default=slug_maker)
#....,
ファイル内で、スラッグ生成のための独立した関数「slug_maker」を定義します
slug_makerでは使用済みの値を返さぬようにチェックするコードを加えています
SlugField内の「blank=True, null=True」を改め、
default=関数名
とし、デフォルト値を設定します
また、「unique=True」とすることで、同じ値が存在しないようにバリデーションをかけています
マイグレーションを経て、エラーが出なければデフォルト値の設定は完了です
テストの実行で確認しよう!
✔views.py > NippoListView
class NippoListView(ListView): #クラス作成
template_name = "nippo/nippo-list.html" #変数
model = NippoModel #変数
def get_queryset(self):
q = self.request.GET.get("search")
qs = NippoModel.objects.search(query=q)
if self.request.user.is_authenticated:
qs = qs.filter(Q(public=True)|Q(user=self.request.user))
else:
qs = qs.filter(public=True)
return qs
スラッグに変更を加えたことで、既存の関数で訂正すべき箇所もありますが、↓の通り、きちんとスラッグが保存されているかをテストする関数を追加しましょう
def test_slug_saved(self):
nippo_obj = NippoModel.objects.first()
self.assertTrue(nippo_obj.slug)
訂正する箇所もあり、test.py全体はこんな感じになりました
from django.test import TestCase
from nippo.models import NippoModel
from django.urls import reverse
from django.contrib.auth import get_user_model
from allauth.account.models import EmailAddress
User = get_user_model()
class NippoTestCase(TestCase):
def __init__(self, *args, **kwargs):
self.email = "test@itc.tokyo"
self.password = "somepassword"
self.title = "testTitle1"
self.content = "testContent1"
self.slug = "some-slug-for-test"
super().__init__(*args, **kwargs)
#初期設定
def setUp(self):
user_obj = User(email=self.email)
user_obj.set_password(self.password)
user_obj.save()
email_obj = EmailAddress(user=user_obj, email=self.email, verified=True)
email_obj.save()
self.user_obj = user_obj
nippo_obj = NippoModel(user=user_obj, title=self.title, content=self.content)
nippo_obj.save()
#日報の作成ができているか
def test_saved_single_object(self):
qs_counter = NippoModel.objects.count()
self.assertEqual(qs_counter, 1)
#ユーザーが作られているか
def test_user_saved(self):
counter = User.objects.count()
self.assertEqual(counter, 1)
email_counter = EmailAddress.objects.count()
self.assertEqual(email_counter, 1)
#メールがverifiedになっているか
def test_email_verified(self):
email_obj = EmailAddress.objects.first()
self.assertEqual(True, email_obj.verified)
#ログインページが機能しているか
def test_login(self):
data = {"email": self.email, "password": self.password}
response = self.client.post("/accounts/login/", data)
self.assertEqual(response.status_code, 200)
#新規登録ページが機能しているか
def test_signup(self):
new_data = {"email": "test2@itc.tokyo", "password1": "somepassword2", "password2": "somepassword2"}
redirect_to = reverse("account_email_verification_sent")
response = self.client.post("/accounts/signup/", new_data)
#Email Confirmationへredirectされているか?
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, redirect_to)
#ユーザーが作られているか?
self.assertEqual(User.objects.count(), 2)
#queryが存在しない時に、404ページを返すかどうか
def test_response_404(self):
detail_url = reverse('nippo-detail', kwargs={"slug": "slug-not-exist"})
detail_response = self.client.get(detail_url)
update_url = reverse('nippo-update', kwargs={"slug": "slug-not-exist"})
update_response = self.client.get(update_url)
delete_url = reverse('nippo-delete', kwargs={"slug": "slug-not-exist"})
delete_response = self.client.get(delete_url)
self.assertEqual(detail_response.status_code, 404)
self.assertEqual(update_response.status_code, 404)
self.assertEqual(delete_response.status_code, 404)
#Anonymousユーザーはアクセスできない
def test_access_to_createview(self):
url = reverse("nippo-create")
redirect_to = reverse("account_login")
response = self.client.get(url)
self.assertRedirects(response, f"{redirect_to}?next=/nippo/create/")
#ログインユーザーが日報を作成する
def test_create_on_createView(self):
user_obj = User.objects.first()
url = reverse('nippo-create')
create_data = {"user": user_obj, "title": "title_from_test", "content": "content_from_test", "slug":"some-random-slug"}
self.client.login(email=self.email, password=self.password)
response = self.client.post(url, create_data)
redirect_to = reverse("nippo-list")
qs_counter2 = NippoModel.objects.count()
self.assertRedirects(response, redirect_to)
self.assertEqual(qs_counter2, 2)
#別のユーザーではアップデートできない
def test_update_with_another_user(self):
another_user = User(email="test2@itc.tokyo")
another_user.set_password("somepassword2")
another_user.save()
self.client.login(email=another_user.email, password=another_user.password)
nippo_obj = NippoModel.objects.first()
redirect_to = reverse("nippo-detail", kwargs={"slug":nippo_obj.slug})
url = reverse('nippo-update', kwargs={"slug": nippo_obj.slug})
response = self.client.get(url)
self.assertRedirects(response, redirect_to)
#自分の日報はアップデートできる
def test_update_with_own_user(self):
self.client.login(email=self.email, password=self.password)
nippo_obj = NippoModel.objects.first()
url = reverse('nippo-update', kwargs={"slug": nippo_obj.slug})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
def test_delete_with_another_user(self):
another_user = User(email="test2@itc.tokyo")
another_user.set_password("somepassword2")
another_user.save()
self.client.login(email=another_user.email, password=another_user.password)
nippo_obj = NippoModel.objects.first()
redirect_to = reverse("nippo-detail", kwargs={"slug":nippo_obj.slug})
url = reverse('nippo-delete', kwargs={"slug": nippo_obj.slug})
response = self.client.post(url, {})
self.assertRedirects(response, redirect_to)
def test_delete_with_own_user(self):
redirect_to = reverse("nippo-list")
self.client.login(email=self.email, password=self.password)
nippo_obj = NippoModel.objects.first()
url = reverse('nippo-delete', kwargs={"slug": nippo_obj.slug})
response = self.client.post(url, {})
self.assertRedirects(response, redirect_to)
def test_listview_with_anonymous(self):
self.client.logout()#logoutの実行
url = reverse("nippo-list")
response = self.client.get(url)
object_list = response.context_data["object_list"]
self.assertEqual(len(object_list), 0)
def test_listview_with_own_user(self):
url = reverse("nippo-list")
self.client.login(email=self.email, password=self.password)
response = self.client.get(url)
object_list = response.context_data["object_list"]
self.assertEqual(len(object_list), 1)
#日報を作成する(テストではない)
def make_nippo(self, user, public):
nippo_obj = NippoModel(user=user, public=public)
nippo_obj.title = f"title {nippo_obj.pk}"
nippo_obj.content = f"content {nippo_obj.pk}"
nippo_obj.save()
return nippo_obj
#サーチフォームテスト
def test_search_queryset(self):
user_obj = User.objects.get(email=self.email)
test_user = User(email="test2@itc.tokyo")
test_user.set_password("somepassword2")
test_user.save()
obj1 = self.make_nippo(user=user_obj, public=True)
obj2 = self.make_nippo(user=user_obj, public=False)
obj3 = self.make_nippo(user=test_user, public=True)
obj4 = self.make_nippo(user=test_user, public=False)
counter = NippoModel.objects.count()
self.assertEqual(counter, 5)
self.client.login(email=self.email, password=self.password)
response = self.client.get(reverse("nippo-list"))
self.assertEqual(len(response.context_data["object_list"]), 4)
url = reverse("nippo-list") + "?search=1"
response = self.client.get(url)
self.assertEqual(len(response.context_data["object_list"]), 1)
url = reverse("nippo-list") + "?search=content"
response = self.client.get(url)
self.assertEqual(len(response.context_data["object_list"]), 4)
#SlugFieldにデフォルト値が格納されているか
def test_slug_saved(self):
nippo_obj = NippoModel.objects.first()
self.assertTrue(nippo_obj.slug)
まとめ
前回のSlugFieldを作成・設定から続き、デフォルト値を関数で設定する方法について解説しました
関数では、
- returnで設定したい値を返すこと
- default=関数名と「 () 」は除いて記述すること
を覚えて置きましょう
次回は、作成日を記録する為の「DateField」、日付をカレンダー入力する「datepicker」について解説していきます