The blog application for django CMS providing multilingual blog functionality with advanced content management features
—
Core data models for blog functionality including posts, categories, and CMS plugins with full multilingual support and SEO integration.
from typing import Optional, List, Dict, Any
from datetime import datetime
from django.db import models
from django.db.models import QuerySet
from django.contrib.auth.models import AbstractUser
from django.http import HttpRequest
from django.utils.functional import cached_property
from cms.models import CMSPlugin, PlaceholderField
from parler.models import TranslatableModel
from meta.models import ModelMeta
from aldryn_apphooks_config.fields import AppHookConfigField
from aldryn_apphooks_config.managers.parler import AppHookConfigTranslatableManager
from djangocms_blog.models import Post, BlogCategory
from djangocms_blog.cms_appconfig import BlogConfig
from djangocms_blog.managers import GenericDateTaggedManager
from djangocms_blog.settings import get_settingThe main Post model provides comprehensive blog post functionality with multilingual content, placeholder fields, meta tag support, and extensive relationship management.
class Post(KnockerModel, BlogMetaMixin, TranslatableModel):
"""
Main blog post model with multilingual and meta support.
Translated fields (via django-parler):
- title: CharField(max_length=752)
- slug: SlugField(max_length=752, blank=True, default='')
- abstract: HTMLField(blank=True, default='')
- meta_description: TextField(blank=True, default='')
- meta_keywords: TextField(blank=True, default='')
- meta_title: CharField(max_length=2000, blank=True, default='')
- post_text: HTMLField(blank=True, default='')
- subtitle: CharField(max_length=767, blank=True, default='')
Non-translated fields:
- author: ForeignKey(User, on_delete=PROTECT, null=True, blank=True)
- date_created: DateTimeField(auto_now_add=True)
- date_modified: DateTimeField(auto_now=True)
- date_published: DateTimeField(null=True, blank=True)
- date_published_end: DateTimeField(null=True, blank=True)
- date_featured: DateTimeField(null=True, blank=True)
- publish: BooleanField(default=False)
- featured: BooleanField(default=False)
- include_in_rss: BooleanField(default=True)
- enable_comments: BooleanField(default=get_setting('ENABLE_COMMENTS'))
- enable_liveblog: BooleanField(default=False)
- sites: ManyToManyField(Site, blank=True)
- app_config: AppHookConfigField(BlogConfig, null=True)
- categories: ManyToManyField(BlogCategory, blank=True)
- tags: TaggableManager(blank=True)
- related: SortedManyToManyField('self', blank=True, symmetrical=False)
- main_image: FilerImageField(null=True, blank=True, on_delete=SET_NULL)
- main_image_thumbnail: ForeignKey(ThumbnailOption, null=True, blank=True, on_delete=SET_NULL)
- main_image_full: ForeignKey(ThumbnailOption, null=True, blank=True, on_delete=SET_NULL)
- content: PlaceholderField('post_content', related_name='post_content')
- media: PlaceholderField('media', related_name='media')
- liveblog: PlaceholderField('live_blog', related_name='live_blog')
"""
# Key methods
def get_absolute_url(self, lang: Optional[str] = None) -> str:
"""Return the absolute URL for the post."""
def get_full_url(self) -> str:
"""Return the full URL including domain."""
def get_image_full_url(self) -> str:
"""Return full URL of the main image."""
def get_image_width(self) -> Optional[int]:
"""Return main image width in pixels."""
def get_image_height(self) -> Optional[int]:
"""Return main image height in pixels."""
def get_title(self) -> str:
"""Return post title with meta fallback."""
def get_description(self) -> str:
"""Return meta description with abstract fallback."""
def get_keywords(self) -> List[str]:
"""Return meta keywords as list."""
def get_tags(self) -> str:
"""Return tags as comma-separated string."""
def get_author(self) -> Optional[AbstractUser]:
"""Return author user object."""
def thumbnail_options(self) -> Dict[str, Any]:
"""Return thumbnail configuration options."""
def full_image_options(self) -> Dict[str, Any]:
"""Return full image configuration options."""
def get_admin_url(self) -> str:
"""Return admin edit URL for the post."""
def should_knock(self, signal_type: str, created: bool = False) -> bool:
"""Return whether to send notification for this post."""
def get_cache_key(self, language: str, prefix: str) -> str:
"""Generate cache key for post in given language."""
@property
def is_published(self) -> bool:
"""Check if post is published."""
@property
def guid(self) -> str:
"""Return unique post identifier hash."""
@property
def date(self) -> datetime:
"""Return featured date or published date."""
@property
def liveblog_group(self) -> str:
"""Return liveblog group identifier."""
# Manager (accessed via Post.objects)
objects: GenericDateTaggedManager
# Manager methods:
# Post.objects.published(current_site=True) - Return QuerySet of published posts
# Post.objects.published_on_rss(current_site=True) - Return RSS-enabled posts
# Post.objects.published_future(current_site=True) - Return published including future
# Post.objects.archived(current_site=True) - Return QuerySet of archived posts
# Post.objects.available(current_site=True) - Return QuerySet of available posts for current site
# Post.objects.filter_by_language(language, current_site=True) - Language filtering
# Post.objects.on_site(site=None) - Site filtering
# Post.objects.get_months(queryset=None, current_site=True) - Archive months with counts
# Post.objects.tagged(other_model=None, queryset=None) - Get tagged items
# Post.objects.tag_list(other_model=None, queryset=None) - Get common tags
# Post.objects.tag_cloud(other_model=None, queryset=None, published=True, on_site=False) - Generate tag cloudMultilingual category model for organizing blog posts with SEO support and hierarchical relationships.
class BlogCategory(BlogMetaMixin, TranslatableModel):
"""
Blog category model with multilingual support.
Translated fields:
- name: CharField(max_length=752)
- slug: SlugField(max_length=752, blank=True, db_index=True)
- meta_description: TextField(blank=True, default='')
Non-translated fields:
- parent: ForeignKey('self', null=True, blank=True, on_delete=CASCADE, related_name='children')
- date_created: DateTimeField(auto_now_add=True)
- date_modified: DateTimeField(auto_now=True)
- app_config: AppHookConfigField(BlogConfig, null=True)
"""
def get_absolute_url(self, lang: Optional[str] = None) -> str:
"""Return the absolute URL for the category."""
def get_full_url(self) -> str:
"""Return the full URL including domain."""
def descendants(self) -> List['BlogCategory']:
"""Return all descendant categories recursively."""
def get_title(self) -> str:
"""Return category title."""
def get_description(self) -> str:
"""Return category meta description."""
@cached_property
def linked_posts(self) -> QuerySet:
"""Return posts in this category for current namespace."""
@cached_property
def count(self) -> int:
"""Return count of posts in this category."""
@cached_property
def count_all_sites(self) -> int:
"""Return count of posts across all sites."""
# Manager (accessed via BlogCategory.objects)
objects: AppHookConfigTranslatableManagerModels for CMS plugins that embed blog content in CMS pages.
class BasePostPlugin(CMSPlugin):
"""Base model for blog-related CMS plugins."""
app_config: AppHookConfigField(BlogConfig, null=True, blank=True)
current_site: BooleanField(default=True)
def optimize(self, qs: QuerySet) -> QuerySet:
"""Apply select_related/prefetch_related optimizations to queryset."""
def post_queryset(self, request: Optional[HttpRequest] = None, published_only: bool = True, selected_posts: Optional[List[int]] = None) -> QuerySet:
"""Get filtered post queryset based on plugin configuration."""
class LatestPostsPlugin(BasePostPlugin):
"""Plugin model for latest posts display."""
latest_posts: IntegerField(default=get_setting('LATEST_POSTS'))
tags: SortedManyToManyField(Tag, blank=True)
categories: SortedManyToManyField(BlogCategory, blank=True)
def get_posts(self, request: HttpRequest, published_only: bool = True) -> QuerySet:
"""Get posts for latest entries plugin."""
class AuthorEntriesPlugin(BasePostPlugin):
"""Plugin model for author posts display."""
authors: SortedManyToManyField(User, verbose_name=_('Authors'), blank=True)
latest_posts: IntegerField(default=get_setting('LATEST_POSTS'))
def get_posts(self, request: HttpRequest, published_only: bool = True) -> QuerySet:
"""Get posts for author entries plugin."""
def get_authors(self, request: HttpRequest) -> QuerySet:
"""Get authors with post counts."""
class FeaturedPostsPlugin(BasePostPlugin):
"""Plugin model for featured posts display."""
featured_posts: IntegerField(default=get_setting('LATEST_POSTS'))
def get_posts(self, request: HttpRequest, published_only: bool = True) -> QuerySet:
"""Get posts for featured posts plugin."""
class GenericBlogPlugin(BasePostPlugin):
"""Generic blog plugin model base."""
cache: BooleanField(default=True)Base mixin providing SEO meta tag functionality via django-meta integration.
class BlogMetaMixin(ModelMeta):
"""
Meta mixin for SEO meta tags using django-meta.
Provides meta tag functionality for models including:
- OpenGraph tags
- Twitter Card tags
- Google+ tags
- Schema.org structured data
"""
def get_meta_title(self) -> str:
"""Return meta title for SEO."""
def get_meta_description(self) -> str:
"""Return meta description for SEO."""
def get_meta_keywords(self) -> str:
"""Return meta keywords for SEO."""
def get_meta_url(self) -> str:
"""Return canonical URL for meta tags."""
def get_meta_image(self) -> str:
"""Return meta image URL for social sharing."""# Creating and managing blog posts
from djangocms_blog.models import Post, BlogCategory
from django.contrib.auth import get_user_model
from django.utils import timezone
User = get_user_model()
# Create a category
category = BlogCategory.objects.create()
category.set_current_language('en')
category.name = 'Technology'
category.slug = 'technology'
category.meta_description = 'Technology related posts'
category.save()
# Create a blog post
post = Post.objects.create(
author=User.objects.first(),
app_config=BlogConfig.objects.first(),
date_published=timezone.now(),
enable_comments=True,
featured=True
)
# Set translated content
post.set_current_language('en')
post.title = 'Introduction to Django CMS'
post.slug = 'introduction-django-cms'
post.abstract = '<p>Learn the basics of Django CMS</p>'
post.post_text = '<p>Django CMS is a powerful content management system...</p>'
post.meta_description = 'Learn Django CMS basics in this comprehensive guide'
post.meta_title = 'Django CMS Tutorial - Complete Guide'
post.save()
# Add to category and tags
post.categories.add(category)
post.tags.add('django', 'cms', 'tutorial')
# Query posts
published_posts = Post.objects.published()
featured_posts = Post.objects.published().filter(featured=True)
category_posts = Post.objects.published().filter(categories=category)
author_posts = Post.objects.published().filter(author=some_user)
# Access post URLs and metadata
url = post.get_absolute_url()
full_url = post.get_full_url()
meta_title = post.get_meta_title()
is_published = post.is_publishedInstall with Tessl CLI
npx tessl i tessl/pypi-djangocms-blog