from django.db import models from django.utils import timezone from django.utils.text import slugify class BlogAuthor(models.Model): """Model for blog post authors""" name = models.CharField(max_length=200) email = models.EmailField(unique=True, blank=True, null=True) bio = models.TextField(blank=True) avatar = models.ImageField(upload_to='blog/authors/', blank=True, null=True) is_active = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = "Blog Author" verbose_name_plural = "Blog Authors" ordering = ['name'] def __str__(self): return self.name class BlogCategory(models.Model): """Model for blog post categories""" title = models.CharField(max_length=100) slug = models.SlugField(max_length=100, unique=True) description = models.TextField(blank=True) display_order = models.PositiveIntegerField(default=0) is_active = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = "Blog Category" verbose_name_plural = "Blog Categories" ordering = ['display_order', 'title'] def __str__(self): return self.title def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.title) super().save(*args, **kwargs) class BlogTag(models.Model): """Model for blog post tags""" name = models.CharField(max_length=50, unique=True) slug = models.SlugField(max_length=50, unique=True) is_active = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: verbose_name = "Blog Tag" verbose_name_plural = "Blog Tags" ordering = ['name'] def __str__(self): return self.name def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.name) super().save(*args, **kwargs) class BlogPost(models.Model): """Model for blog posts""" title = models.CharField(max_length=300) slug = models.SlugField(max_length=300, unique=True) content = models.TextField() excerpt = models.TextField(blank=True, help_text="Short excerpt for preview") thumbnail = models.ImageField(upload_to='blog/thumbnails/', blank=True, null=True) thumbnail_url = models.CharField(max_length=500, blank=True, help_text="External thumbnail URL") featured_image = models.ImageField(upload_to='blog/featured/', blank=True, null=True) featured_image_url = models.CharField(max_length=500, blank=True, help_text="External featured image URL") author = models.ForeignKey( BlogAuthor, on_delete=models.SET_NULL, null=True, related_name='posts' ) category = models.ForeignKey( BlogCategory, on_delete=models.SET_NULL, null=True, related_name='posts' ) tags = models.ManyToManyField(BlogTag, related_name='posts', blank=True) # SEO and metadata meta_description = models.CharField(max_length=160, blank=True) meta_keywords = models.CharField(max_length=255, blank=True) # Status and visibility published = models.BooleanField(default=True) featured = models.BooleanField(default=False, help_text="Featured post") views_count = models.PositiveIntegerField(default=0) reading_time = models.PositiveIntegerField(default=5, help_text="Estimated reading time in minutes") # Timestamps published_at = models.DateTimeField(default=timezone.now) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = "Blog Post" verbose_name_plural = "Blog Posts" ordering = ['-published_at', '-created_at'] indexes = [ models.Index(fields=['-published_at']), models.Index(fields=['slug']), models.Index(fields=['published']), ] def __str__(self): return self.title def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.title) if not self.excerpt and self.content: # Generate excerpt from content (first 200 characters) self.excerpt = self.content[:200] + '...' if len(self.content) > 200 else self.content super().save(*args, **kwargs) @property def get_thumbnail_url(self): """Return the thumbnail URL (uploaded image or external URL)""" if self.thumbnail and hasattr(self.thumbnail, 'url'): return self.thumbnail.url elif self.thumbnail_url: return self.thumbnail_url return None @property def get_featured_image_url(self): """Return the featured image URL (uploaded image or external URL)""" if self.featured_image and hasattr(self.featured_image, 'url'): return self.featured_image.url elif self.featured_image_url: return self.featured_image_url return None def increment_views(self): """Increment the view count""" self.views_count += 1 self.save(update_fields=['views_count']) class BlogComment(models.Model): """Model for blog post comments""" post = models.ForeignKey(BlogPost, on_delete=models.CASCADE, related_name='comments') name = models.CharField(max_length=100) email = models.EmailField() content = models.TextField() parent = models.ForeignKey( 'self', on_delete=models.CASCADE, null=True, blank=True, related_name='replies' ) is_approved = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = "Blog Comment" verbose_name_plural = "Blog Comments" ordering = ['-created_at'] def __str__(self): return f"Comment by {self.name} on {self.post.title}"