from django.db import models from django.conf import settings from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from .managers import CommentThreadManager, ThreadedModelManager class CommentThread(models.Model): """ A thread of comments that can be attached to any model instance, including historical versions. """ content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') title = models.CharField(max_length=255, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name='created_comment_threads' ) is_locked = models.BooleanField(default=False) is_hidden = models.BooleanField(default=False) objects = CommentThreadManager() class Meta: indexes = [ models.Index(fields=['content_type', 'object_id']), ] ordering = ['-created_at'] def __str__(self): return f"Comment Thread on {self.content_object} - {self.title}" class CommentThreads: """ Helper class to manage comment threads for a model instance. This is used instead of direct inheritance to avoid historical model issues. """ def __init__(self, instance): self.instance = instance self._info = {} def get_info(self): """Get or compute comment thread information.""" if not self._info: ct = ContentType.objects.get_for_model(self.instance.__class__) self._info = { 'count': CommentThread.objects.filter( content_type=ct, object_id=self.instance.pk ).count(), 'content_type': ct, 'object_id': self.instance.pk } return self._info def get_threads(self): """Get comment threads for this instance.""" info = self.get_info() return CommentThread.objects.filter( content_type=info['content_type'], object_id=info['object_id'] ) def add_thread(self, title='', created_by=None): """Create a new comment thread for this instance.""" info = self.get_info() thread = CommentThread.objects.create( content_type=info['content_type'], object_id=info['object_id'], title=title, created_by=created_by ) self._info = {} # Clear cache return thread def get_comment_threads(instance): """Get or create a CommentThreads helper for a model instance.""" if not hasattr(instance, '_comment_threads'): instance._comment_threads = CommentThreads(instance) return instance._comment_threads class Comment(models.Model): """Individual comment within a thread.""" thread = models.ForeignKey( CommentThread, on_delete=models.CASCADE, related_name='comments' ) author = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name='comments' ) content = models.TextField() created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) is_edited = models.BooleanField(default=False) is_hidden = models.BooleanField(default=False) parent = models.ForeignKey( 'self', on_delete=models.CASCADE, null=True, blank=True, related_name='replies' ) class Meta: ordering = ['created_at'] def __str__(self): return f"Comment by {self.author} on {self.created_at}"