0
# Model Base Classes
1
2
Django Extensions provides abstract model classes that implement common functionality patterns including timestamps, activation status, and title/description/slug combinations. These base classes reduce boilerplate code and provide consistent implementations of frequently needed model patterns.
3
4
## Capabilities
5
6
### TimeStampedModel
7
8
Abstract base class that provides self-managed "created" and "modified" fields with automatic timestamp handling.
9
10
```python { .api }
11
class TimeStampedModel(models.Model):
12
created: CreationDateTimeField # Automatically set on creation
13
modified: ModificationDateTimeField # Automatically updated on save
14
15
def save(self, update_modified=True, **kwargs):
16
"""
17
Save the model instance.
18
19
Parameters:
20
- update_modified: If False, prevents modification timestamp update
21
"""
22
23
class Meta:
24
get_latest_by = 'modified'
25
abstract = True
26
```
27
28
Usage examples:
29
30
```python
31
from django.db import models
32
from django_extensions.db.models import TimeStampedModel
33
34
class Article(TimeStampedModel):
35
title = models.CharField(max_length=200)
36
content = models.TextField()
37
# created and modified fields are automatically added
38
39
class BlogPost(TimeStampedModel):
40
title = models.CharField(max_length=200)
41
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
42
43
def save_without_timestamp_update(self, *args, **kwargs):
44
# Save without updating the modified timestamp
45
self.update_modified = False
46
self.save(*args, **kwargs)
47
48
# Usage
49
article = Article.objects.create(title="My Article", content="Content")
50
print(article.created) # DateTime when created
51
print(article.modified) # Same as created initially
52
53
article.content = "Updated content"
54
article.save()
55
print(article.modified) # Updated to current time
56
57
# Get latest article
58
latest = Article.objects.latest() # Uses 'modified' field by default
59
```
60
61
### TitleDescriptionModel
62
63
Abstract base class that provides standard title and description fields.
64
65
```python { .api }
66
class TitleDescriptionModel(models.Model):
67
title: CharField # max_length=255
68
description: TextField # blank=True, null=True
69
70
class Meta:
71
abstract = True
72
```
73
74
Usage examples:
75
76
```python
77
from django.db import models
78
from django_extensions.db.models import TitleDescriptionModel
79
80
class Category(TitleDescriptionModel):
81
# Inherits title and description fields
82
color = models.CharField(max_length=7) # For hex color codes
83
84
class Product(TitleDescriptionModel):
85
price = models.DecimalField(max_digits=10, decimal_places=2)
86
sku = models.CharField(max_length=50, unique=True)
87
88
# Usage
89
category = Category.objects.create(
90
title="Electronics",
91
description="Electronic devices and accessories",
92
color="#3498db"
93
)
94
```
95
96
### TitleSlugDescriptionModel
97
98
Abstract base class that extends TitleDescriptionModel with an auto-generated slug field.
99
100
```python { .api }
101
class TitleSlugDescriptionModel(TitleDescriptionModel):
102
title: CharField # Inherited from TitleDescriptionModel
103
description: TextField # Inherited from TitleDescriptionModel
104
slug: AutoSlugField # Auto-populated from 'title'
105
106
# Custom slugify function can be defined on the model
107
def slugify_function(self, content):
108
"""
109
Optional: Define custom slugification logic.
110
111
Parameters:
112
- content: The content to be slugified
113
114
Returns:
115
- str: The slugified content
116
"""
117
118
class Meta:
119
abstract = True
120
```
121
122
Usage examples:
123
124
```python
125
from django.db import models
126
from django_extensions.db.models import TitleSlugDescriptionModel
127
128
class Page(TitleSlugDescriptionModel):
129
content = models.TextField()
130
is_published = models.BooleanField(default=False)
131
132
class CustomSlugPage(TitleSlugDescriptionModel):
133
content = models.TextField()
134
135
def slugify_function(self, content):
136
# Custom slugification: use underscores instead of hyphens
137
return content.replace(' ', '_').lower()
138
139
# Usage
140
page = Page.objects.create(
141
title="About Us",
142
description="Information about our company",
143
content="We are a great company..."
144
)
145
print(page.slug) # "about-us" (automatically generated)
146
147
# With custom slugify function
148
custom_page = CustomSlugPage.objects.create(
149
title="Contact Info",
150
content="Our contact information"
151
)
152
print(custom_page.slug) # "contact_info" (uses custom function)
153
```
154
155
### ActivatorModel System
156
157
A complete system for managing model activation status with querysets and managers.
158
159
```python { .api }
160
class ActivatorQuerySet(models.QuerySet):
161
def active(self):
162
"""Return queryset filtered to active instances only."""
163
164
def inactive(self):
165
"""Return queryset filtered to inactive instances only."""
166
167
class ActivatorModelManager(models.Manager):
168
def get_queryset(self):
169
"""Use ActivatorQuerySet for all queries."""
170
171
def active(self):
172
"""Return active instances: Model.objects.active()"""
173
174
def inactive(self):
175
"""Return inactive instances: Model.objects.inactive()"""
176
177
class ActivatorModel(models.Model):
178
INACTIVE_STATUS: int = 0
179
ACTIVE_STATUS: int = 1
180
STATUS_CHOICES: tuple = (
181
(INACTIVE_STATUS, 'Inactive'),
182
(ACTIVE_STATUS, 'Active')
183
)
184
185
status: IntegerField # choices=STATUS_CHOICES, default=ACTIVE_STATUS
186
activate_date: DateTimeField # blank=True, null=True
187
deactivate_date: DateTimeField # blank=True, null=True
188
objects: ActivatorModelManager
189
190
def save(self, *args, **kwargs):
191
"""Automatically set activate_date if not provided."""
192
193
class Meta:
194
ordering = ('status', '-activate_date')
195
abstract = True
196
```
197
198
Usage examples:
199
200
```python
201
from django.db import models
202
from django_extensions.db.models import ActivatorModel
203
from django.utils import timezone
204
205
class Campaign(ActivatorModel):
206
name = models.CharField(max_length=200)
207
budget = models.DecimalField(max_digits=10, decimal_places=2)
208
209
class Promotion(ActivatorModel):
210
title = models.CharField(max_length=200)
211
discount_percent = models.IntegerField()
212
213
# Usage - Creating instances
214
campaign = Campaign.objects.create(
215
name="Summer Sale",
216
budget=5000.00,
217
# status defaults to ACTIVE_STATUS
218
# activate_date is automatically set to now()
219
)
220
221
# Deactivating a campaign
222
campaign.status = Campaign.INACTIVE_STATUS
223
campaign.deactivate_date = timezone.now()
224
campaign.save()
225
226
# Querying active/inactive instances
227
active_campaigns = Campaign.objects.active()
228
inactive_campaigns = Campaign.objects.inactive()
229
230
# Using in templates or serializers
231
for campaign in Campaign.objects.active():
232
print(f"Active: {campaign.name}")
233
234
# Scheduled activation
235
future_campaign = Campaign.objects.create(
236
name="Holiday Sale",
237
budget=10000.00,
238
activate_date=timezone.now() + timezone.timedelta(days=30)
239
)
240
```
241
242
## Combining Base Classes
243
244
Base classes can be combined to create models with multiple features:
245
246
```python
247
from django.db import models
248
from django_extensions.db.models import TimeStampedModel, ActivatorModel
249
250
# Multiple inheritance
251
class ManagedContent(TimeStampedModel, ActivatorModel):
252
title = models.CharField(max_length=200)
253
content = models.TextField()
254
255
class Meta:
256
# Inherit ordering from ActivatorModel, add created
257
ordering = ('status', '-activate_date', '-created')
258
259
# Custom combination with TitleSlugDescriptionModel
260
class PublishableArticle(TitleSlugDescriptionModel, TimeStampedModel, ActivatorModel):
261
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
262
featured_image = models.ImageField(upload_to='articles/', blank=True)
263
264
def is_published(self):
265
return self.status == self.ACTIVE_STATUS
266
267
@classmethod
268
def published(cls):
269
return cls.objects.active()
270
271
# Usage
272
article = PublishableArticle.objects.create(
273
title="Django Best Practices",
274
description="A guide to Django development",
275
author=request.user
276
)
277
# Has: title, slug, description, created, modified, status, activate_date, deactivate_date
278
279
published_articles = PublishableArticle.published()
280
```
281
282
## Model Patterns and Best Practices
283
284
```python
285
# Pattern 1: Timestamped content with soft delete
286
class SoftDeleteTimeStampedModel(TimeStampedModel):
287
is_deleted = models.BooleanField(default=False)
288
deleted_at = models.DateTimeField(null=True, blank=True)
289
290
def delete(self, using=None, keep_parents=False):
291
# Soft delete instead of actual deletion
292
self.is_deleted = True
293
self.deleted_at = timezone.now()
294
self.save()
295
296
class Meta:
297
abstract = True
298
299
# Pattern 2: Hierarchical categories with timestamps
300
class Category(TitleSlugDescriptionModel, TimeStampedModel):
301
parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)
302
order = models.PositiveIntegerField(default=0)
303
304
class Meta:
305
ordering = ['order', 'title']
306
verbose_name_plural = 'categories'
307
308
# Pattern 3: User-owned content with activation
309
class UserContent(TitleDescriptionModel, ActivatorModel, TimeStampedModel):
310
owner = models.ForeignKey('auth.User', on_delete=models.CASCADE)
311
is_public = models.BooleanField(default=False)
312
313
def can_view(self, user):
314
if self.status != self.ACTIVE_STATUS:
315
return False
316
return self.is_public or self.owner == user or user.is_staff
317
318
class Meta:
319
abstract = True
320
```
321
322
## Manager and QuerySet Customization
323
324
```python
325
# Extending ActivatorModel with custom methods
326
class PublishedQuerySet(ActivatorQuerySet):
327
def published(self):
328
return self.active().filter(publish_date__lte=timezone.now())
329
330
def featured(self):
331
return self.published().filter(is_featured=True)
332
333
class PublishedManager(ActivatorModelManager):
334
def get_queryset(self):
335
return PublishedQuerySet(self.model, using=self._db)
336
337
def published(self):
338
return self.get_queryset().published()
339
340
def featured(self):
341
return self.get_queryset().featured()
342
343
class Article(TitleSlugDescriptionModel, ActivatorModel, TimeStampedModel):
344
content = models.TextField()
345
publish_date = models.DateTimeField(default=timezone.now)
346
is_featured = models.BooleanField(default=False)
347
348
objects = PublishedManager()
349
350
class Meta:
351
ordering = ['-publish_date']
352
353
# Usage
354
Article.objects.published() # Active articles with publish_date <= now
355
Article.objects.featured() # Published + featured articles
356
```