The blog application for django CMS providing multilingual blog functionality with advanced content management features
—
Advanced admin interface with placeholder editing, frontend editing, and comprehensive form handling for posts, categories, and configuration.
Comprehensive admin interface for blog post management with multilingual support and advanced editing features.
class PostAdmin(PlaceholderAdminMixin, FrontendEditableAdminMixin, ModelAppHookConfig, TranslatableAdmin):
"""
Admin interface for blog posts.
Features:
- Multilingual editing with django-parler
- Placeholder field editing
- Frontend editing support
- Rich media management
- SEO field management
- Advanced filtering and search
"""
form: Type[PostAdminForm] = PostAdminForm
list_display: List[str] = [
'title', 'author', 'date_published', 'app_config', 'is_published'
]
list_filter: List[str] = [
'app_config', 'categories', 'date_published', 'featured'
]
search_fields: List[str] = ['translations__title', 'translations__abstract']
filter_horizontal: List[str] = ['categories', 'related']
readonly_fields: List[str] = ['created', 'modified']
date_hierarchy: str = 'date_published'
fieldsets: List[Tuple[str, Dict[str, Any]]] = [
(None, {
'fields': (
'title', 'subtitle', 'slug', 'abstract', 'post_text',
'author', 'date_published', 'date_featured',
'enable_comments', 'featured', 'liveblog', 'include_in_rss'
)
}),
('Images', {
'fields': ('main_image', 'main_image_thumbnail', 'main_image_full'),
'classes': ('collapse',)
}),
('SEO', {
'fields': ('meta_title', 'meta_description', 'meta_keywords'),
'classes': ('collapse',)
}),
('Relationships', {
'fields': ('categories', 'tags', 'related', 'sites'),
'classes': ('collapse',)
}),
('Advanced', {
'fields': ('app_config', 'created', 'modified'),
'classes': ('collapse',)
})
]
def get_queryset(self, request: HttpRequest) -> QuerySet:
"""Return queryset filtered by app config permissions."""
def get_form(self, request: HttpRequest, obj: Optional[Post] = None, **kwargs) -> Type[ModelForm]:
"""Return form class with proper configuration."""
def save_model(self, request: HttpRequest, obj: Post, form: ModelForm, change: bool) -> None:
"""Save model with additional processing."""
def get_prepopulated_fields(self, request: HttpRequest, obj: Optional[Post] = None) -> Dict[str, List[str]]:
"""Return fields for automatic slug population."""
def is_published(self, obj: Post) -> bool:
"""Check if post is published for list display."""
is_published.boolean = True
is_published.short_description = 'Published'Admin interface for blog category management with hierarchical support.
class BlogCategoryAdmin(ModelAppHookConfig, TranslatableAdmin):
"""
Admin interface for blog categories.
Features:
- Multilingual category management
- Hierarchical category organization
- SEO field management
- App configuration filtering
"""
form: Type[CategoryAdminForm] = CategoryAdminForm
list_display: List[str] = ['name', 'app_config', 'parent']
list_filter: List[str] = ['app_config', 'parent']
search_fields: List[str] = ['translations__name']
fieldsets: List[Tuple[str, Dict[str, Any]]] = [
(None, {
'fields': ('name', 'slug', 'parent', 'app_config')
}),
('SEO', {
'fields': ('meta_title', 'meta_description', 'meta_keywords'),
'classes': ('collapse',)
})
]
def get_prepopulated_fields(self, request: HttpRequest, obj: Optional[BlogCategory] = None) -> Dict[str, List[str]]:
"""Return fields for automatic slug population."""Admin interface for blog configuration management.
class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin):
"""
Admin interface for blog configuration.
Features:
- Per-apphook configuration
- Multilingual configuration options
- Template and permalink settings
- SEO and social media settings
"""
def namespace_valid(self, obj: BlogConfig) -> bool:
"""Validate namespace configuration."""Custom admin filter for multi-site support.
class SiteListFilter(admin.SimpleListFilter):
"""
Admin list filter for site-specific filtering.
Features:
- Filter posts by site
- Multi-site deployment support
- Permission-based filtering
"""
title: str = 'Site'
parameter_name: str = 'site'
def lookups(self, request: HttpRequest, model_admin: admin.ModelAdmin) -> List[Tuple[str, str]]:
"""Return available site options."""
def queryset(self, request: HttpRequest, queryset: QuerySet) -> QuerySet:
"""Filter queryset by selected site."""Comprehensive form classes for blog content management.
class ConfigFormBase:
"""
Base class for configuration forms.
Features:
- App configuration filtering
- Dynamic field generation
- Validation helpers
"""
def __init__(self, **kwargs) -> None:
"""Initialize form with app config filtering."""
def filter_by_app_config(self, field_name: str, queryset: QuerySet) -> QuerySet:
"""Filter form field queryset by app configuration."""
class PostAdminFormBase(ConfigFormBase, TranslatableModelForm):
"""
Base admin form for posts.
Features:
- Multilingual form handling
- Category and tag management
- SEO field validation
- Slug generation
"""
class Meta:
model: Type[Post] = Post
fields: str = '__all__'
def __init__(self, *args, **kwargs) -> None:
"""Initialize form with proper field configuration."""
def clean_slug(self) -> str:
"""Validate and generate slug."""
def clean(self) -> Dict[str, Any]:
"""Perform cross-field validation."""
class PostAdminForm(PostAdminFormBase):
"""
Main admin form for blog posts.
Fields:
- All Post model fields
- Custom validation logic
- Dynamic field filtering
"""
def __init__(self, *args, **kwargs) -> None:
"""Initialize with complete field set."""
class CategoryAdminForm(ConfigFormBase, TranslatableModelForm):
"""
Admin form for blog categories.
Features:
- Hierarchical category selection
- App configuration filtering
- SEO field management
"""
class Meta:
model: Type[BlogCategory] = BlogCategory
fields: str = '__all__'
class BlogPluginForm(forms.ModelForm):
"""
Base form for blog plugins.
Features:
- Template selection
- App configuration filtering
- Plugin-specific validation
"""
class Meta:
model: Type[BasePostPlugin] = BasePostPlugin
fields: str = '__all__'
class LatestEntriesForm(BlogPluginForm):
"""
Form for latest entries plugin.
Fields:
- latest_posts: IntegerField
- tags: ModelMultipleChoiceField
- categories: ModelMultipleChoiceField
- template: ChoiceField
"""
class Meta:
model: Type[LatestPostsPlugin] = LatestPostsPlugin
fields: str = '__all__'
class AuthorPostsForm(BlogPluginForm):
"""
Form for author posts plugin.
Fields:
- authors: ModelMultipleChoiceField
- latest_posts: IntegerField
- template: ChoiceField
"""
class Meta:
model: Type[AuthorEntriesPlugin] = AuthorEntriesPlugin
fields: str = '__all__'# Customizing admin interface
from django.contrib import admin
from djangocms_blog.models import Post, BlogCategory
from djangocms_blog.admin import PostAdmin, BlogCategoryAdmin
# Customize post admin
class CustomPostAdmin(PostAdmin):
"""Custom post admin with additional features."""
list_display = PostAdmin.list_display + ['custom_field']
list_filter = PostAdmin.list_filter + ['custom_category']
fieldsets = PostAdmin.fieldsets + [
('Custom Fields', {
'fields': ('custom_field', 'custom_category'),
'classes': ('collapse',)
})
]
def get_queryset(self, request):
"""Custom queryset filtering."""
qs = super().get_queryset(request)
if not request.user.is_superuser:
qs = qs.filter(author=request.user)
return qs
# Re-register with custom admin
admin.site.unregister(Post)
admin.site.register(Post, CustomPostAdmin)
# Custom form validation
from djangocms_blog.forms import PostAdminForm
class CustomPostAdminForm(PostAdminForm):
"""Custom post form with additional validation."""
def clean_title(self):
"""Custom title validation."""
title = self.cleaned_data.get('title')
if title and len(title) < 10:
raise forms.ValidationError('Title must be at least 10 characters long.')
return title
def clean(self):
"""Custom cross-field validation."""
cleaned_data = super().clean()
date_published = cleaned_data.get('date_published')
featured = cleaned_data.get('featured')
if featured and not date_published:
raise forms.ValidationError('Featured posts must have a publication date.')
return cleaned_data
# Using forms in custom views
from djangocms_blog.forms import PostAdminForm
from django.views.generic import CreateView
class CustomPostCreateView(CreateView):
"""Custom view for post creation."""
model = Post
form_class = PostAdminForm
template_name = 'custom/post_create.html'
def form_valid(self, form):
"""Set author before saving."""
form.instance.author = self.request.user
return super().form_valid(form)
# Admin action examples
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
@admin.action(description=_('Mark selected posts as featured'))
def make_featured(modeladmin, request, queryset):
"""Admin action to mark posts as featured."""
queryset.update(featured=True)
@admin.action(description=_('Publish selected posts'))
def publish_posts(modeladmin, request, queryset):
"""Admin action to publish posts."""
from django.utils import timezone
queryset.update(date_published=timezone.now())
# Add actions to admin
class EnhancedPostAdmin(PostAdmin):
actions = [make_featured, publish_posts]
# Custom admin templates
# Create templates in:
# templates/admin/djangocms_blog/post/change_form.html
# templates/admin/djangocms_blog/post/change_list.htmlInstall with Tessl CLI
npx tessl i tessl/pypi-djangocms-blog