website/hub/services/models/articles.py

120 lines
4 KiB
Python
Raw Normal View History

2025-06-06 14:53:49 +02:00
from django.db import models
from django.urls import reverse
from django.utils.text import slugify
from django.contrib.auth.models import User
2025-07-04 15:51:44 +02:00
from django.utils import timezone
2025-07-08 16:24:28 +02:00
from .base import validate_image_size, get_prose_editor_field
2025-06-06 14:53:49 +02:00
from .services import Service
from .providers import CloudProvider, ConsultingPartner
2025-07-04 17:26:09 +02:00
from .images import ImageReference
2025-06-06 14:53:49 +02:00
2025-07-04 17:26:09 +02:00
class Article(ImageReference):
2025-06-06 14:53:49 +02:00
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=250, unique=True)
excerpt = models.TextField(
max_length=500, help_text="Brief description of the article"
)
2025-07-08 16:24:28 +02:00
content = get_prose_editor_field()
2025-06-06 14:53:49 +02:00
meta_keywords = models.CharField(
max_length=255, blank=True, help_text="SEO keywords separated by commas"
)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="articles")
2025-07-04 15:51:44 +02:00
article_date = models.DateField(
default=timezone.now, help_text="Date of the article publishing"
)
2025-06-06 14:53:49 +02:00
# Relations to other models
related_service = models.ForeignKey(
Service,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="articles",
help_text="Link this article to a specific service",
)
related_consulting_partner = models.ForeignKey(
ConsultingPartner,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="articles",
help_text="Link this article to a consulting partner",
)
related_cloud_provider = models.ForeignKey(
CloudProvider,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="articles",
help_text="Link this article to a cloud provider",
)
# Open Graph image for social sharing
og_image = models.ImageField(
upload_to="article_og_images/",
blank=True,
null=True,
validators=[validate_image_size],
2025-07-08 16:24:28 +02:00
help_text="Optional Open Graph image for social sharing (max 1MB). If not provided, the article's main image will be used.",
)
2025-06-06 14:53:49 +02:00
# Publishing controls
is_published = models.BooleanField(
default=False, help_text="Only published articles are visible to users"
)
is_featured = models.BooleanField(
default=False, help_text="Featured articles appear prominently in listings"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ["-created_at"]
verbose_name = "Article"
verbose_name_plural = "Articles"
def __str__(self):
return self.title
def save(self, *args, **kwargs):
# Auto-generate slug from title if not provided
if not self.slug:
self.slug = slugify(self.title)
counter = 1
while Article.objects.filter(slug=self.slug).exists():
self.slug = f"{slugify(self.title)}-{counter}"
counter += 1
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse("services:article_detail", kwargs={"slug": self.slug})
2025-07-04 17:26:09 +02:00
@property
def get_image(self):
"""Returns the image from the library"""
2025-07-04 17:26:09 +02:00
if self.image_library and self.image_library.image:
return self.image_library.image
return None
2025-07-04 17:26:09 +02:00
@property
def get_og_image(self):
"""Returns the Open Graph image for social sharing"""
# Use specific OG image if available
if self.og_image:
return self.og_image
# Fall back to main article image
return self.get_image
2025-06-06 14:53:49 +02:00
@property
def related_to(self):
"""Returns a string describing what this article is related to"""
if self.related_service:
return f"Service: {self.related_service.name}"
elif self.related_consulting_partner:
return f"Partner: {self.related_consulting_partner.name}"
elif self.related_cloud_provider:
return f"Provider: {self.related_cloud_provider.name}"
return "General"