0
# Template Tags and Utilities
1
2
Template tags for extracting media content from posts, utility functions, and helper classes for template integration and content management.
3
4
## Capabilities
5
6
### Media Template Tags
7
8
Template tags for working with media attachments in blog posts.
9
10
```python { .api }
11
@register.simple_tag(name="media_plugins", takes_context=True)
12
def media_plugins(context: Dict[str, Any], post: Post) -> List[CMSPlugin]:
13
"""
14
Extract MediaAttachmentPluginMixin plugins from post media placeholder.
15
16
Parameters:
17
- context: Template context dictionary
18
- post: Post instance to extract media from
19
20
Returns:
21
List of MediaAttachmentPluginMixin plugins from the media placeholder
22
23
Usage in templates:
24
{% media_plugins post as media_plugins %}
25
{% for plugin in media_plugins %}{% render_plugin plugin %}{% endfor %}
26
"""
27
28
@register.simple_tag(name="media_images", takes_context=True)
29
def media_images(context: Dict[str, Any], post: Post, main: bool = True) -> List[str]:
30
"""
31
Extract images from MediaAttachmentPluginMixin plugins in post media placeholder.
32
33
Parameters:
34
- context: Template context dictionary
35
- post: Post instance to extract images from
36
- main: If True, get main images; if False, get thumbnail images
37
38
Returns:
39
List of image URLs from media plugins
40
41
Features:
42
- Supports main images and thumbnails
43
- Handles djangocms-video poster field fallback
44
- Graceful error handling for missing methods
45
46
Usage in templates:
47
{% media_images post False as thumbs %}
48
{% for thumb in thumbs %}<img src="{{ thumb }}"/>{% endfor %}
49
50
{% media_images post as main_images %}
51
{% for image in main_images %}<img src="{{ image }}"/>{% endfor %}
52
"""
53
```
54
55
### Utility Functions
56
57
Core utility functions for slug generation and text processing.
58
59
```python { .api }
60
def slugify(base: str) -> str:
61
"""
62
Generate URL-friendly slugs with unicode support.
63
64
Parameters:
65
- base: String to convert to slug
66
67
Returns:
68
URL-friendly slug string
69
70
Features:
71
- Respects BLOG_UNICODE_SLUGS setting
72
- Uses Django's slugify with unicode support
73
- Consistent slug generation across the application
74
"""
75
```
76
77
### Manager Utilities
78
79
Custom manager classes for advanced querying and filtering.
80
81
```python { .api }
82
class TaggedFilterItem:
83
"""
84
Manager mixin for tag filtering functionality.
85
86
Features:
87
- Tag-based content filtering
88
- Related content discovery
89
- Cross-model tag relationships
90
"""
91
92
def tagged(self, other_model: Optional[models.Model] = None, queryset: Optional[QuerySet] = None) -> QuerySet:
93
"""
94
Return queryset of items tagged with same tags as other model/queryset.
95
96
Parameters:
97
- other_model: Model instance to match tags with
98
- queryset: QuerySet to match tags with
99
100
Returns:
101
QuerySet filtered by matching tags
102
"""
103
104
def _taglist(self, other_model: Optional[models.Model] = None, queryset: Optional[QuerySet] = None) -> List[int]:
105
"""
106
Return list of tag IDs common to current model and provided model/queryset.
107
108
Parameters:
109
- other_model: Model instance to find common tags with
110
- queryset: QuerySet to find common tags with
111
112
Returns:
113
List of tag IDs that are common between models
114
"""
115
116
def tag_list(self, other_model: Optional[models.Model] = None, queryset: Optional[QuerySet] = None) -> QuerySet:
117
"""
118
Return queryset of tags common to current model and provided model/queryset.
119
120
Parameters:
121
- other_model: Model instance to find common tags with
122
- queryset: QuerySet to find common tags with
123
124
Returns:
125
QuerySet of Tag objects that are common
126
"""
127
128
class GenericDateTaggedManager(TaggedFilterItem, AppHookConfigTranslatableManager):
129
"""
130
Manager combining date filtering, tag filtering, and app config support.
131
132
Features:
133
- App config filtering
134
- Multilingual content support
135
- Tag-based filtering
136
- Date-based filtering
137
- Publication status filtering
138
"""
139
140
def published(self) -> QuerySet:
141
"""Return published posts queryset."""
142
143
def archived(self) -> QuerySet:
144
"""Return archived posts queryset."""
145
146
def available(self) -> QuerySet:
147
"""Return posts available for current site."""
148
149
def by_author(self, author: AbstractUser) -> QuerySet:
150
"""Return posts by specific author."""
151
152
def by_category(self, category: BlogCategory) -> QuerySet:
153
"""Return posts in specific category."""
154
155
def by_tag(self, tag: str) -> QuerySet:
156
"""Return posts with specific tag."""
157
158
def featured(self) -> QuerySet:
159
"""Return featured posts."""
160
161
def recent(self, count: int = 5) -> QuerySet:
162
"""Return recent posts."""
163
```
164
165
### Template Context Processors
166
167
Context processors for adding blog-related data to template contexts.
168
169
```python { .api }
170
# Context data automatically available in templates when using blog views:
171
# - post: Current post object (in detail views)
172
# - post_list: List of posts (in list views)
173
# - categories: Available categories
174
# - tags: Available tags
175
# - config: Current blog configuration
176
# - meta: Meta information for SEO
177
# - view: Current view instance
178
```
179
180
## Template Integration Examples
181
182
```python
183
# Using template tags in templates
184
# Load the template tags
185
{% load cms_tags djangocms_blog %}
186
187
# Extract and display media plugins
188
{% media_plugins post as media_plugins %}
189
{% if media_plugins %}
190
<div class="post-media">
191
{% for plugin in media_plugins %}
192
{% render_plugin plugin %}
193
{% endfor %}
194
</div>
195
{% endif %}
196
197
# Display media images
198
{% media_images post as main_images %}
199
{% if main_images %}
200
<div class="post-images">
201
{% for image in main_images %}
202
<img src="{{ image }}" alt="{{ post.title }}" class="post-image">
203
{% endfor %}
204
</div>
205
{% endif %}
206
207
# Display thumbnail images
208
{% media_images post False as thumbs %}
209
{% if thumbs %}
210
<div class="post-thumbnails">
211
{% for thumb in thumbs %}
212
<img src="{{ thumb }}" alt="{{ post.title }}" class="post-thumb">
213
{% endfor %}
214
</div>
215
{% endif %}
216
217
# Custom template tag usage
218
from django import template
219
from djangocms_blog.models import Post
220
from djangocms_blog.templatetags.djangocms_blog import media_images
221
222
register = template.Library()
223
224
@register.simple_tag
225
def post_gallery(post, image_type='main'):
226
"""Custom tag for post image gallery."""
227
if image_type == 'main':
228
images = media_images({'request': None}, post, main=True)
229
else:
230
images = media_images({'request': None}, post, main=False)
231
232
return {
233
'images': images,
234
'post': post,
235
'image_type': image_type
236
}
237
238
# Using utility functions
239
from djangocms_blog.fields import slugify
240
241
# Generate slugs
242
title = "My Awesome Blog Post with Ñoño Characters"
243
slug = slugify(title) # Returns "my-awesome-blog-post-with-ñoño-characters"
244
245
# Custom slug generation
246
def generate_unique_slug(title, model_class, instance=None):
247
"""Generate unique slug for model instance."""
248
base_slug = slugify(title)
249
slug = base_slug
250
counter = 1
251
252
queryset = model_class.objects.all()
253
if instance and instance.pk:
254
queryset = queryset.exclude(pk=instance.pk)
255
256
while queryset.filter(slug=slug).exists():
257
slug = f"{base_slug}-{counter}"
258
counter += 1
259
260
return slug
261
262
# Using manager utilities
263
from djangocms_blog.models import Post
264
265
# Find posts with similar tags
266
main_post = Post.objects.get(pk=1)
267
related_posts = Post.objects.tagged(main_post)[:5]
268
269
# Find posts tagged with same tags as a set of posts
270
featured_posts = Post.objects.featured()
271
similar_posts = Post.objects.tagged(queryset=featured_posts)
272
273
# Complex querying with manager
274
from djangocms_blog.managers import GenericDateTaggedManager
275
276
class CustomPostManager(GenericDateTaggedManager):
277
"""Custom manager with additional methods."""
278
279
def by_year(self, year):
280
"""Get posts by year."""
281
return self.published().filter(date_published__year=year)
282
283
def popular(self, limit=10):
284
"""Get popular posts (example implementation)."""
285
return self.published().order_by('-views')[:limit]
286
287
def with_images(self):
288
"""Get posts that have main images."""
289
return self.published().exclude(main_image__isnull=True)
290
291
# Template context usage
292
# In templates, access manager methods:
293
# {% for post in view.get_queryset.featured %}
294
# {% for post in view.get_queryset.by_author:request.user %}
295
# {% for post in view.get_queryset.recent:10 %}
296
297
# Custom template filters
298
from django import template
299
from django.utils.safestring import mark_safe
300
from djangocms_blog.fields import slugify
301
302
register = template.Library()
303
304
@register.filter
305
def blog_slugify(value):
306
"""Template filter for slug generation."""
307
return slugify(str(value))
308
309
@register.filter
310
def post_excerpt(post, length=150):
311
"""Generate post excerpt."""
312
if post.abstract:
313
text = post.abstract
314
elif post.post_text:
315
text = post.post_text
316
else:
317
return ""
318
319
# Strip HTML and truncate
320
from django.utils.html import strip_tags
321
from django.utils.text import Truncator
322
323
plain_text = strip_tags(text)
324
truncated = Truncator(plain_text).chars(length, truncate='...')
325
return mark_safe(truncated)
326
327
# Usage in templates:
328
# {{ post.title|blog_slugify }}
329
# {{ post|post_excerpt:200 }}
330
331
# Custom inclusion tags
332
@register.inclusion_tag('blog/tags/related_posts.html', takes_context=True)
333
def related_posts(context, post, count=5):
334
"""Inclusion tag for related posts."""
335
related = Post.objects.published().tagged(post).exclude(pk=post.pk)[:count]
336
337
return {
338
'related_posts': related,
339
'current_post': post,
340
'request': context.get('request')
341
}
342
343
# Usage: {% related_posts post 3 %}
344
```