0
# Models and Data Management
1
2
Core data models for blog functionality including posts, categories, and CMS plugins with full multilingual support and SEO integration.
3
4
## Imports
5
6
```python
7
from typing import Optional, List, Dict, Any
8
from datetime import datetime
9
from django.db import models
10
from django.db.models import QuerySet
11
from django.contrib.auth.models import AbstractUser
12
from django.http import HttpRequest
13
from django.utils.functional import cached_property
14
from cms.models import CMSPlugin, PlaceholderField
15
from parler.models import TranslatableModel
16
from meta.models import ModelMeta
17
from aldryn_apphooks_config.fields import AppHookConfigField
18
from aldryn_apphooks_config.managers.parler import AppHookConfigTranslatableManager
19
from djangocms_blog.models import Post, BlogCategory
20
from djangocms_blog.cms_appconfig import BlogConfig
21
from djangocms_blog.managers import GenericDateTaggedManager
22
from djangocms_blog.settings import get_setting
23
```
24
25
## Capabilities
26
27
### Blog Post Model
28
29
The main Post model provides comprehensive blog post functionality with multilingual content, placeholder fields, meta tag support, and extensive relationship management.
30
31
```python { .api }
32
class Post(KnockerModel, BlogMetaMixin, TranslatableModel):
33
"""
34
Main blog post model with multilingual and meta support.
35
36
Translated fields (via django-parler):
37
- title: CharField(max_length=752)
38
- slug: SlugField(max_length=752, blank=True, default='')
39
- abstract: HTMLField(blank=True, default='')
40
- meta_description: TextField(blank=True, default='')
41
- meta_keywords: TextField(blank=True, default='')
42
- meta_title: CharField(max_length=2000, blank=True, default='')
43
- post_text: HTMLField(blank=True, default='')
44
- subtitle: CharField(max_length=767, blank=True, default='')
45
46
Non-translated fields:
47
- author: ForeignKey(User, on_delete=PROTECT, null=True, blank=True)
48
- date_created: DateTimeField(auto_now_add=True)
49
- date_modified: DateTimeField(auto_now=True)
50
- date_published: DateTimeField(null=True, blank=True)
51
- date_published_end: DateTimeField(null=True, blank=True)
52
- date_featured: DateTimeField(null=True, blank=True)
53
- publish: BooleanField(default=False)
54
- featured: BooleanField(default=False)
55
- include_in_rss: BooleanField(default=True)
56
- enable_comments: BooleanField(default=get_setting('ENABLE_COMMENTS'))
57
- enable_liveblog: BooleanField(default=False)
58
- sites: ManyToManyField(Site, blank=True)
59
- app_config: AppHookConfigField(BlogConfig, null=True)
60
- categories: ManyToManyField(BlogCategory, blank=True)
61
- tags: TaggableManager(blank=True)
62
- related: SortedManyToManyField('self', blank=True, symmetrical=False)
63
- main_image: FilerImageField(null=True, blank=True, on_delete=SET_NULL)
64
- main_image_thumbnail: ForeignKey(ThumbnailOption, null=True, blank=True, on_delete=SET_NULL)
65
- main_image_full: ForeignKey(ThumbnailOption, null=True, blank=True, on_delete=SET_NULL)
66
- content: PlaceholderField('post_content', related_name='post_content')
67
- media: PlaceholderField('media', related_name='media')
68
- liveblog: PlaceholderField('live_blog', related_name='live_blog')
69
"""
70
71
# Key methods
72
def get_absolute_url(self, lang: Optional[str] = None) -> str:
73
"""Return the absolute URL for the post."""
74
75
def get_full_url(self) -> str:
76
"""Return the full URL including domain."""
77
78
def get_image_full_url(self) -> str:
79
"""Return full URL of the main image."""
80
81
def get_image_width(self) -> Optional[int]:
82
"""Return main image width in pixels."""
83
84
def get_image_height(self) -> Optional[int]:
85
"""Return main image height in pixels."""
86
87
def get_title(self) -> str:
88
"""Return post title with meta fallback."""
89
90
def get_description(self) -> str:
91
"""Return meta description with abstract fallback."""
92
93
def get_keywords(self) -> List[str]:
94
"""Return meta keywords as list."""
95
96
def get_tags(self) -> str:
97
"""Return tags as comma-separated string."""
98
99
def get_author(self) -> Optional[AbstractUser]:
100
"""Return author user object."""
101
102
def thumbnail_options(self) -> Dict[str, Any]:
103
"""Return thumbnail configuration options."""
104
105
def full_image_options(self) -> Dict[str, Any]:
106
"""Return full image configuration options."""
107
108
def get_admin_url(self) -> str:
109
"""Return admin edit URL for the post."""
110
111
def should_knock(self, signal_type: str, created: bool = False) -> bool:
112
"""Return whether to send notification for this post."""
113
114
def get_cache_key(self, language: str, prefix: str) -> str:
115
"""Generate cache key for post in given language."""
116
117
@property
118
def is_published(self) -> bool:
119
"""Check if post is published."""
120
121
@property
122
def guid(self) -> str:
123
"""Return unique post identifier hash."""
124
125
@property
126
def date(self) -> datetime:
127
"""Return featured date or published date."""
128
129
@property
130
def liveblog_group(self) -> str:
131
"""Return liveblog group identifier."""
132
133
# Manager (accessed via Post.objects)
134
objects: GenericDateTaggedManager
135
136
# Manager methods:
137
# Post.objects.published(current_site=True) - Return QuerySet of published posts
138
# Post.objects.published_on_rss(current_site=True) - Return RSS-enabled posts
139
# Post.objects.published_future(current_site=True) - Return published including future
140
# Post.objects.archived(current_site=True) - Return QuerySet of archived posts
141
# Post.objects.available(current_site=True) - Return QuerySet of available posts for current site
142
# Post.objects.filter_by_language(language, current_site=True) - Language filtering
143
# Post.objects.on_site(site=None) - Site filtering
144
# Post.objects.get_months(queryset=None, current_site=True) - Archive months with counts
145
# Post.objects.tagged(other_model=None, queryset=None) - Get tagged items
146
# Post.objects.tag_list(other_model=None, queryset=None) - Get common tags
147
# Post.objects.tag_cloud(other_model=None, queryset=None, published=True, on_site=False) - Generate tag cloud
148
```
149
150
### Blog Category Model
151
152
Multilingual category model for organizing blog posts with SEO support and hierarchical relationships.
153
154
```python { .api }
155
class BlogCategory(BlogMetaMixin, TranslatableModel):
156
"""
157
Blog category model with multilingual support.
158
159
Translated fields:
160
- name: CharField(max_length=752)
161
- slug: SlugField(max_length=752, blank=True, db_index=True)
162
- meta_description: TextField(blank=True, default='')
163
164
Non-translated fields:
165
- parent: ForeignKey('self', null=True, blank=True, on_delete=CASCADE, related_name='children')
166
- date_created: DateTimeField(auto_now_add=True)
167
- date_modified: DateTimeField(auto_now=True)
168
- app_config: AppHookConfigField(BlogConfig, null=True)
169
"""
170
171
def get_absolute_url(self, lang: Optional[str] = None) -> str:
172
"""Return the absolute URL for the category."""
173
174
def get_full_url(self) -> str:
175
"""Return the full URL including domain."""
176
177
def descendants(self) -> List['BlogCategory']:
178
"""Return all descendant categories recursively."""
179
180
def get_title(self) -> str:
181
"""Return category title."""
182
183
def get_description(self) -> str:
184
"""Return category meta description."""
185
186
@cached_property
187
def linked_posts(self) -> QuerySet:
188
"""Return posts in this category for current namespace."""
189
190
@cached_property
191
def count(self) -> int:
192
"""Return count of posts in this category."""
193
194
@cached_property
195
def count_all_sites(self) -> int:
196
"""Return count of posts across all sites."""
197
198
# Manager (accessed via BlogCategory.objects)
199
objects: AppHookConfigTranslatableManager
200
```
201
202
### CMS Plugin Models
203
204
Models for CMS plugins that embed blog content in CMS pages.
205
206
```python { .api }
207
class BasePostPlugin(CMSPlugin):
208
"""Base model for blog-related CMS plugins."""
209
app_config: AppHookConfigField(BlogConfig, null=True, blank=True)
210
current_site: BooleanField(default=True)
211
212
def optimize(self, qs: QuerySet) -> QuerySet:
213
"""Apply select_related/prefetch_related optimizations to queryset."""
214
215
def post_queryset(self, request: Optional[HttpRequest] = None, published_only: bool = True, selected_posts: Optional[List[int]] = None) -> QuerySet:
216
"""Get filtered post queryset based on plugin configuration."""
217
218
class LatestPostsPlugin(BasePostPlugin):
219
"""Plugin model for latest posts display."""
220
latest_posts: IntegerField(default=get_setting('LATEST_POSTS'))
221
tags: SortedManyToManyField(Tag, blank=True)
222
categories: SortedManyToManyField(BlogCategory, blank=True)
223
224
def get_posts(self, request: HttpRequest, published_only: bool = True) -> QuerySet:
225
"""Get posts for latest entries plugin."""
226
227
class AuthorEntriesPlugin(BasePostPlugin):
228
"""Plugin model for author posts display."""
229
authors: SortedManyToManyField(User, verbose_name=_('Authors'), blank=True)
230
latest_posts: IntegerField(default=get_setting('LATEST_POSTS'))
231
232
def get_posts(self, request: HttpRequest, published_only: bool = True) -> QuerySet:
233
"""Get posts for author entries plugin."""
234
235
def get_authors(self, request: HttpRequest) -> QuerySet:
236
"""Get authors with post counts."""
237
238
class FeaturedPostsPlugin(BasePostPlugin):
239
"""Plugin model for featured posts display."""
240
featured_posts: IntegerField(default=get_setting('LATEST_POSTS'))
241
242
def get_posts(self, request: HttpRequest, published_only: bool = True) -> QuerySet:
243
"""Get posts for featured posts plugin."""
244
245
class GenericBlogPlugin(BasePostPlugin):
246
"""Generic blog plugin model base."""
247
cache: BooleanField(default=True)
248
```
249
250
### Meta Mixin
251
252
Base mixin providing SEO meta tag functionality via django-meta integration.
253
254
```python { .api }
255
class BlogMetaMixin(ModelMeta):
256
"""
257
Meta mixin for SEO meta tags using django-meta.
258
259
Provides meta tag functionality for models including:
260
- OpenGraph tags
261
- Twitter Card tags
262
- Google+ tags
263
- Schema.org structured data
264
"""
265
266
def get_meta_title(self) -> str:
267
"""Return meta title for SEO."""
268
269
def get_meta_description(self) -> str:
270
"""Return meta description for SEO."""
271
272
def get_meta_keywords(self) -> str:
273
"""Return meta keywords for SEO."""
274
275
def get_meta_url(self) -> str:
276
"""Return canonical URL for meta tags."""
277
278
def get_meta_image(self) -> str:
279
"""Return meta image URL for social sharing."""
280
```
281
282
## Model Usage Examples
283
284
```python
285
# Creating and managing blog posts
286
from djangocms_blog.models import Post, BlogCategory
287
from django.contrib.auth import get_user_model
288
from django.utils import timezone
289
290
User = get_user_model()
291
292
# Create a category
293
category = BlogCategory.objects.create()
294
category.set_current_language('en')
295
category.name = 'Technology'
296
category.slug = 'technology'
297
category.meta_description = 'Technology related posts'
298
category.save()
299
300
# Create a blog post
301
post = Post.objects.create(
302
author=User.objects.first(),
303
app_config=BlogConfig.objects.first(),
304
date_published=timezone.now(),
305
enable_comments=True,
306
featured=True
307
)
308
309
# Set translated content
310
post.set_current_language('en')
311
post.title = 'Introduction to Django CMS'
312
post.slug = 'introduction-django-cms'
313
post.abstract = '<p>Learn the basics of Django CMS</p>'
314
post.post_text = '<p>Django CMS is a powerful content management system...</p>'
315
post.meta_description = 'Learn Django CMS basics in this comprehensive guide'
316
post.meta_title = 'Django CMS Tutorial - Complete Guide'
317
post.save()
318
319
# Add to category and tags
320
post.categories.add(category)
321
post.tags.add('django', 'cms', 'tutorial')
322
323
# Query posts
324
published_posts = Post.objects.published()
325
featured_posts = Post.objects.published().filter(featured=True)
326
category_posts = Post.objects.published().filter(categories=category)
327
author_posts = Post.objects.published().filter(author=some_user)
328
329
# Access post URLs and metadata
330
url = post.get_absolute_url()
331
full_url = post.get_full_url()
332
meta_title = post.get_meta_title()
333
is_published = post.is_published
334
```