今回の問題
みなさんこんにちは!
駆け出し前エンジニアのYuyaです。
所々ツッコミポイントがあるかもしれませんが
その場合は質問してくださると今後の学習の糧になりますので、よろしくお願い致します。
今、DjangoでXのクローンアプリを作成しています。
プロフィールページでユーザー情報はそのままで
タブでポスト、リポスト、いいね、コメント
この4つを効率良くコードにする方法を探していました。
そもそも継承とは
端的に言うと、継承とはあるクラスが
別に存在しているクラスの性質を受け継いていることです。
一例としては犬があります。
以下がイメージです。
(細かい個体差はここでは考えません)
犬種ごとに毛色や大きさが異なりますが、以下の特徴や振る舞いを持っています。
- 足が四本
- 嗅覚が人間の約100万倍
- 嬉しいと尻尾を振る
フォルダ構成
実際の例を見ていきましょう。
以下が今回の大まかなフォルダ構成です。
manage.py
config/
settings.py
urls.py
apps/
accounts/
urls.py
models.py
views.py
posts/
urls.py
models.py
views.py
services.py
profiles/
urls.py
views.py
services.py
画面遷移イメージ
とりあえずViewに殴り書き
Serviceクラスが書いてありますが
コメントアウトで概要を記載して詳細は割愛します。
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView
from django.shortcuts import get_object_or_404
from apps.accounts.models import CustomUser
from .services import ProfileService
from apps.posts.services import PostService
PROFILE_PAGE = "profile.html"
# ユーザー情報を出力するView
class ProfileView(LoginRequiredMixin, TemplateView):
template_name = PROFILE_PAGE
def get_context_data(self, **kwargs):
# 既存のget_context_dataをコール
context = super().get_context_data(**kwargs)
# ログインユーザーを取得
user = get_object_or_404(CustomUser, id=self.request.user.id)
# フォロー、フォロワー情報をcontextに渡す
context['user_relatinships'] = ProfileService.count_following_followers(user)
return context
# プロフィールユーザーのポスト一覧を出力するView
class PostView(LoginRequiredMixin, TemplateView):
template_name = PROFILE_PAGE
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user = get_object_or_404(CustomUser, id=self.request.user.id)
context['posts'] = PostService.get_my_post_list(user)
return context
今回の問題で記載がある画像の通り
ProfileView
(ユーザー情報)はうまく出力されました。
ただ、ポストタブをクリックした際にユーザー情報が出力されなくなりました。
ProfileView
の中でPostView
を呼びたい
困りました。
これだとユーザー情報が出力されません。
ただ、やることは明確になりました。
ProfileView
の中でPostView
を呼び出せば解決しそうだと考えました!!
ここで継承 & オーバーライド
ここでやっと本題の継承が登場です。
完成のコードをみていきましょう!
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView
from django.shortcuts import get_object_or_404
from apps.accounts.models import CustomUser
from .services import ProfileService
from apps.posts.services import PostService
class BaseProfileView(LoginRequiredMixin ,TemplateView):
template_name = "profile.html"
def get_user_activities(self, user):
""" 子クラスでオーバーライドするためのメソッド """
return []
def get_context_data(self, **kwargs):
# 既存のget_context_dataをコール
context = super().get_context_data(**kwargs)
# ログインユーザーを取得
user = get_object_or_404(CustomUser, id=self.request.user.id)
# ユーザーのプロフィール情報
context['user_data'] = ProfileService.format_user(user)
# ユーザーのフォロー、フォロワー情報
context['user_relatinships'] = ProfileService.count_following_followers(user)
# ユーザーの投稿データ
context['activities'] = self.get_user_activities(user)
return context
# 継承先の BaseProfileView の get_user_activities をオーバーライド
class PostView(BaseProfileView):
def get_user_activities(self, user):
return PostService.get_my_post_list(user)
以下の6ステップで実行されます
-
PostView.get_context_data()
が呼ばれる -
BaseProfileView.get_context_data()
のsuper().get_context_data()
で親のTemplateView.get_context_data()
が実行 -
self.request.user
でログインユーザーを取得 -
context['activities'] = self.get_user_activities(user)
でPostView.get_user_activities(user)
が実行される -
PostService.get_my_post_list(user)
で投稿データを取得 -
context
を返し、テンプレート (template_name
) に渡す
この実行順序でユーザー情報を保ったままタブの切り替えができそうです。
またこの書き方によって、ログインユーザーの取得や
template_name
を複数回書かずに済むといったメリットも享受することができます。
これでポスト一覧が出力できました。
これならリポスト、いいね、コメントの一覧もget_user_activities
をオーバーライドすることで取得してテンプレートで出力できそうです。
最後に
この問題を解決した後に、私は自問自答しました。
いや、お前LoginRequiredMixin
,TemplateView
とか既に継承してんじゃんw
要は継承やオーバーライドで何が行われているかという
基礎の理解はできているが
実用ができていないことが浮き彫りになりました。
この考えをストックしたということに着目して
これからも学習を続けていきます!