-
複数のアプリ(ブログ、掲示板、相性表など)で、ほぼ同じ
Comment
/Reply
モデルを重複定義していた。 -
各モデルに
name, text, created_at, image, video
のような同一フィールドが散らばっており、仕様変更時に複数箇所直す必要があった。 -
追加機能を実装したいときも、すべてのモデルにフィールドを追加する作業が面倒。
投稿日:2025年10月07日
最近、自分の Django アプリ(KakuhanApp)で複数ページに似たようなコメント機能を実装していたら、修正や機能追加のたびに複数ファイルを直す必要があって面倒になってきました。
そこで「共通部分を抽象化して core
アプリにまとめる」リファクタリングを行うことで、今後の拡張や修正を楽にしたいと考えました。この記事はその手順やポイントをまとめたものです。
複数のアプリ(ブログ、掲示板、相性表など)で、ほぼ同じ Comment
/ Reply
モデルを重複定義していた。
各モデルに name, text, created_at, image, video
のような同一フィールドが散らばっており、仕様変更時に複数箇所直す必要があった。
追加機能を実装したいときも、すべてのモデルにフィールドを追加する作業が面倒。
共通フィールド(name, text, created_at, image, video
)を core
に BaseComment
/ BaseReply
として抽象化(abstract = True
)。
各アプリ側のモデルはその Base
を継承し、固有の外部キー(例:blog
/ topic
/ character
)や掲示板固有フィールドだけを宣言する。
テンプレート(コメント一覧の HTML)は既存のものを変更せず使えるように配慮する(comment.replies.all
などを維持)。
3-1. 元の重複モデル
class BlogComment(models.Model): # ブログ用モデル
blog = models.ForeignKey(BlogPost, on_delete=models.CASCADE, related_name="comments")
name = models.CharField(max_length=100, default="匿名")
text = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self): return f"{self.name}: {self.text[:20]}"
class ForumComment(models.Model): # 雑談掲示板用モデル
topic = models.ForeignKey(Topic, related_name="comments", on_delete=models.CASCADE)
name = models.CharField(max_length=100, default="匿名")
text = models.TextField()
image = models.ImageField(upload_to="forum_comments/images/", blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self): return f"{self.name}: {self.text[:20]}"
このように似た項目がファイルごとに散らばっていました。
3-2. 作った共通基底(core/models/base_comment.py
)
class BaseComment(models.Model):
name = models.CharField(max_length=100, default="匿名")
text = models.TextField()
image = models.ImageField(upload_to="comments/images/", blank=True, null=True)
video = models.FileField(upload_to="comments/videos/", blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True
def __str__(self):
return f"{self.name}: {self.text[:20]}"
ポイント
abstract = True
にしているので core
自体にテーブルは作られない。
3-3. 各アプリ側の子モデルへ継承
class BlogComment(BaseComment): # ブログ用モデル
blog = models.ForeignKey(BlogPost, on_delete=models.CASCADE, related_name="comments")
class Comment(BaseComment): # 雑談掲示板用モデル
topic = models.ForeignKey(Topic, related_name="comments", on_delete=models.CASCADE)
image = models.ImageField(upload_to="forum_comments/images/", blank=True, null=True)
ポイント
子モデルで image
/video
を再定義することで、以前と同じ upload_to
を保持できる(既存メディアのパスを変えたくないとき)。
基底クラス(抽象クラス)を導入したことで得られた具体的なメリットを挙げます。
機能追加や修正が1箇所で済む
例えば「コメントにいいね機能を追加したい」といった変更も、BaseComment
(基底クラス)にフラグフィールドや表示用メソッドを追加するだけで対応できます。これにより、各アプリのコメントモデルを個別に修正する必要がなくなります。
コード重複が減る
各コメントモデルで同じフィールド定義を繰り返す必要がなくなり、共通部分はすべて抽象クラスに集約できます。結果として、コードがより簡潔で保守しやすくなりました。
新たなページへの追加が容易
新しいページにコメント機能を追加する際も、BaseComment
を継承するだけで済むため、開発コストを大幅に削減できます。
このように、「基底クラス(抽象クラス)」を導入することで、Djangoアプリ全体の保守性・拡張性・再利用性が格段に向上しました。
今回は「まずは共通化して土台を固める」ことにフォーカスしました。これで次のステップ(機能追加や修正)は、ぐっと簡単になります。
重複を見つけたら一度 abstract
ベースにまとめてみることを強くお勧めします 。