0
# Admin and Forms
1
2
Advanced admin interface with placeholder editing, frontend editing, and comprehensive form handling for posts, categories, and configuration.
3
4
## Capabilities
5
6
### Post Administration
7
8
Comprehensive admin interface for blog post management with multilingual support and advanced editing features.
9
10
```python { .api }
11
class PostAdmin(PlaceholderAdminMixin, FrontendEditableAdminMixin, ModelAppHookConfig, TranslatableAdmin):
12
"""
13
Admin interface for blog posts.
14
15
Features:
16
- Multilingual editing with django-parler
17
- Placeholder field editing
18
- Frontend editing support
19
- Rich media management
20
- SEO field management
21
- Advanced filtering and search
22
"""
23
24
form: Type[PostAdminForm] = PostAdminForm
25
list_display: List[str] = [
26
'title', 'author', 'date_published', 'app_config', 'is_published'
27
]
28
list_filter: List[str] = [
29
'app_config', 'categories', 'date_published', 'featured'
30
]
31
search_fields: List[str] = ['translations__title', 'translations__abstract']
32
filter_horizontal: List[str] = ['categories', 'related']
33
readonly_fields: List[str] = ['created', 'modified']
34
date_hierarchy: str = 'date_published'
35
36
fieldsets: List[Tuple[str, Dict[str, Any]]] = [
37
(None, {
38
'fields': (
39
'title', 'subtitle', 'slug', 'abstract', 'post_text',
40
'author', 'date_published', 'date_featured',
41
'enable_comments', 'featured', 'liveblog', 'include_in_rss'
42
)
43
}),
44
('Images', {
45
'fields': ('main_image', 'main_image_thumbnail', 'main_image_full'),
46
'classes': ('collapse',)
47
}),
48
('SEO', {
49
'fields': ('meta_title', 'meta_description', 'meta_keywords'),
50
'classes': ('collapse',)
51
}),
52
('Relationships', {
53
'fields': ('categories', 'tags', 'related', 'sites'),
54
'classes': ('collapse',)
55
}),
56
('Advanced', {
57
'fields': ('app_config', 'created', 'modified'),
58
'classes': ('collapse',)
59
})
60
]
61
62
def get_queryset(self, request: HttpRequest) -> QuerySet:
63
"""Return queryset filtered by app config permissions."""
64
65
def get_form(self, request: HttpRequest, obj: Optional[Post] = None, **kwargs) -> Type[ModelForm]:
66
"""Return form class with proper configuration."""
67
68
def save_model(self, request: HttpRequest, obj: Post, form: ModelForm, change: bool) -> None:
69
"""Save model with additional processing."""
70
71
def get_prepopulated_fields(self, request: HttpRequest, obj: Optional[Post] = None) -> Dict[str, List[str]]:
72
"""Return fields for automatic slug population."""
73
74
def is_published(self, obj: Post) -> bool:
75
"""Check if post is published for list display."""
76
is_published.boolean = True
77
is_published.short_description = 'Published'
78
```
79
80
### Category Administration
81
82
Admin interface for blog category management with hierarchical support.
83
84
```python { .api }
85
class BlogCategoryAdmin(ModelAppHookConfig, TranslatableAdmin):
86
"""
87
Admin interface for blog categories.
88
89
Features:
90
- Multilingual category management
91
- Hierarchical category organization
92
- SEO field management
93
- App configuration filtering
94
"""
95
96
form: Type[CategoryAdminForm] = CategoryAdminForm
97
list_display: List[str] = ['name', 'app_config', 'parent']
98
list_filter: List[str] = ['app_config', 'parent']
99
search_fields: List[str] = ['translations__name']
100
101
fieldsets: List[Tuple[str, Dict[str, Any]]] = [
102
(None, {
103
'fields': ('name', 'slug', 'parent', 'app_config')
104
}),
105
('SEO', {
106
'fields': ('meta_title', 'meta_description', 'meta_keywords'),
107
'classes': ('collapse',)
108
})
109
]
110
111
def get_prepopulated_fields(self, request: HttpRequest, obj: Optional[BlogCategory] = None) -> Dict[str, List[str]]:
112
"""Return fields for automatic slug population."""
113
```
114
115
### Configuration Administration
116
117
Admin interface for blog configuration management.
118
119
```python { .api }
120
class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin):
121
"""
122
Admin interface for blog configuration.
123
124
Features:
125
- Per-apphook configuration
126
- Multilingual configuration options
127
- Template and permalink settings
128
- SEO and social media settings
129
"""
130
131
def namespace_valid(self, obj: BlogConfig) -> bool:
132
"""Validate namespace configuration."""
133
```
134
135
### Site Filtering
136
137
Custom admin filter for multi-site support.
138
139
```python { .api }
140
class SiteListFilter(admin.SimpleListFilter):
141
"""
142
Admin list filter for site-specific filtering.
143
144
Features:
145
- Filter posts by site
146
- Multi-site deployment support
147
- Permission-based filtering
148
"""
149
150
title: str = 'Site'
151
parameter_name: str = 'site'
152
153
def lookups(self, request: HttpRequest, model_admin: admin.ModelAdmin) -> List[Tuple[str, str]]:
154
"""Return available site options."""
155
156
def queryset(self, request: HttpRequest, queryset: QuerySet) -> QuerySet:
157
"""Filter queryset by selected site."""
158
```
159
160
### Form Classes
161
162
Comprehensive form classes for blog content management.
163
164
```python { .api }
165
class ConfigFormBase:
166
"""
167
Base class for configuration forms.
168
169
Features:
170
- App configuration filtering
171
- Dynamic field generation
172
- Validation helpers
173
"""
174
175
def __init__(self, **kwargs) -> None:
176
"""Initialize form with app config filtering."""
177
178
def filter_by_app_config(self, field_name: str, queryset: QuerySet) -> QuerySet:
179
"""Filter form field queryset by app configuration."""
180
181
class PostAdminFormBase(ConfigFormBase, TranslatableModelForm):
182
"""
183
Base admin form for posts.
184
185
Features:
186
- Multilingual form handling
187
- Category and tag management
188
- SEO field validation
189
- Slug generation
190
"""
191
192
class Meta:
193
model: Type[Post] = Post
194
fields: str = '__all__'
195
196
def __init__(self, *args, **kwargs) -> None:
197
"""Initialize form with proper field configuration."""
198
199
def clean_slug(self) -> str:
200
"""Validate and generate slug."""
201
202
def clean(self) -> Dict[str, Any]:
203
"""Perform cross-field validation."""
204
205
class PostAdminForm(PostAdminFormBase):
206
"""
207
Main admin form for blog posts.
208
209
Fields:
210
- All Post model fields
211
- Custom validation logic
212
- Dynamic field filtering
213
"""
214
215
def __init__(self, *args, **kwargs) -> None:
216
"""Initialize with complete field set."""
217
218
class CategoryAdminForm(ConfigFormBase, TranslatableModelForm):
219
"""
220
Admin form for blog categories.
221
222
Features:
223
- Hierarchical category selection
224
- App configuration filtering
225
- SEO field management
226
"""
227
228
class Meta:
229
model: Type[BlogCategory] = BlogCategory
230
fields: str = '__all__'
231
232
class BlogPluginForm(forms.ModelForm):
233
"""
234
Base form for blog plugins.
235
236
Features:
237
- Template selection
238
- App configuration filtering
239
- Plugin-specific validation
240
"""
241
242
class Meta:
243
model: Type[BasePostPlugin] = BasePostPlugin
244
fields: str = '__all__'
245
246
class LatestEntriesForm(BlogPluginForm):
247
"""
248
Form for latest entries plugin.
249
250
Fields:
251
- latest_posts: IntegerField
252
- tags: ModelMultipleChoiceField
253
- categories: ModelMultipleChoiceField
254
- template: ChoiceField
255
"""
256
257
class Meta:
258
model: Type[LatestPostsPlugin] = LatestPostsPlugin
259
fields: str = '__all__'
260
261
class AuthorPostsForm(BlogPluginForm):
262
"""
263
Form for author posts plugin.
264
265
Fields:
266
- authors: ModelMultipleChoiceField
267
- latest_posts: IntegerField
268
- template: ChoiceField
269
"""
270
271
class Meta:
272
model: Type[AuthorEntriesPlugin] = AuthorEntriesPlugin
273
fields: str = '__all__'
274
```
275
276
## Admin Usage Examples
277
278
```python
279
# Customizing admin interface
280
from django.contrib import admin
281
from djangocms_blog.models import Post, BlogCategory
282
from djangocms_blog.admin import PostAdmin, BlogCategoryAdmin
283
284
# Customize post admin
285
class CustomPostAdmin(PostAdmin):
286
"""Custom post admin with additional features."""
287
288
list_display = PostAdmin.list_display + ['custom_field']
289
list_filter = PostAdmin.list_filter + ['custom_category']
290
291
fieldsets = PostAdmin.fieldsets + [
292
('Custom Fields', {
293
'fields': ('custom_field', 'custom_category'),
294
'classes': ('collapse',)
295
})
296
]
297
298
def get_queryset(self, request):
299
"""Custom queryset filtering."""
300
qs = super().get_queryset(request)
301
if not request.user.is_superuser:
302
qs = qs.filter(author=request.user)
303
return qs
304
305
# Re-register with custom admin
306
admin.site.unregister(Post)
307
admin.site.register(Post, CustomPostAdmin)
308
309
# Custom form validation
310
from djangocms_blog.forms import PostAdminForm
311
312
class CustomPostAdminForm(PostAdminForm):
313
"""Custom post form with additional validation."""
314
315
def clean_title(self):
316
"""Custom title validation."""
317
title = self.cleaned_data.get('title')
318
if title and len(title) < 10:
319
raise forms.ValidationError('Title must be at least 10 characters long.')
320
return title
321
322
def clean(self):
323
"""Custom cross-field validation."""
324
cleaned_data = super().clean()
325
date_published = cleaned_data.get('date_published')
326
featured = cleaned_data.get('featured')
327
328
if featured and not date_published:
329
raise forms.ValidationError('Featured posts must have a publication date.')
330
331
return cleaned_data
332
333
# Using forms in custom views
334
from djangocms_blog.forms import PostAdminForm
335
from django.views.generic import CreateView
336
337
class CustomPostCreateView(CreateView):
338
"""Custom view for post creation."""
339
340
model = Post
341
form_class = PostAdminForm
342
template_name = 'custom/post_create.html'
343
344
def form_valid(self, form):
345
"""Set author before saving."""
346
form.instance.author = self.request.user
347
return super().form_valid(form)
348
349
# Admin action examples
350
from django.contrib import admin
351
from django.utils.translation import gettext_lazy as _
352
353
@admin.action(description=_('Mark selected posts as featured'))
354
def make_featured(modeladmin, request, queryset):
355
"""Admin action to mark posts as featured."""
356
queryset.update(featured=True)
357
358
@admin.action(description=_('Publish selected posts'))
359
def publish_posts(modeladmin, request, queryset):
360
"""Admin action to publish posts."""
361
from django.utils import timezone
362
queryset.update(date_published=timezone.now())
363
364
# Add actions to admin
365
class EnhancedPostAdmin(PostAdmin):
366
actions = [make_featured, publish_posts]
367
368
# Custom admin templates
369
# Create templates in:
370
# templates/admin/djangocms_blog/post/change_form.html
371
# templates/admin/djangocms_blog/post/change_list.html
372
```