0
# Django Integration
1
2
DiskCache provides seamless Django integration through the DjangoCache class, which implements Django's cache backend interface while maintaining all DiskCache features and performance benefits. This allows Django applications to use persistent disk-based caching with minimal configuration changes.
3
4
## Capabilities
5
6
### DjangoCache Class
7
8
A Django-compatible cache backend that wraps FanoutCache while providing the standard Django cache interface.
9
10
```python { .api }
11
class DjangoCache:
12
def __init__(self, directory, params):
13
"""
14
Initialize Django-compatible cache backend.
15
16
Args:
17
directory (str): Cache directory path
18
params (dict): Django cache configuration parameters:
19
- SHARDS (int): Number of shards for FanoutCache. Default 8.
20
- DATABASE_TIMEOUT (float): SQLite timeout. Default 0.010.
21
- OPTIONS (dict): Additional cache options
22
"""
23
24
@property
25
def directory(self):
26
"""Cache directory path."""
27
```
28
29
### Django Cache Interface
30
31
Standard Django cache methods with DiskCache enhancements.
32
33
```python { .api }
34
def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None, read=False, tag=None, retry=True):
35
"""
36
Add key-value pair only if key doesn't exist.
37
38
Args:
39
key (str): Cache key
40
value: Value to store
41
timeout (int): Expiration timeout in seconds
42
version (int, optional): Key version for namespacing
43
read (bool): Store value as file for reading. Default False.
44
tag (str, optional): Tag for grouping related items
45
retry (bool): Retry on timeout. Default True.
46
47
Returns:
48
bool: True if key was added (didn't exist)
49
"""
50
51
def get(self, key, default=None, version=None, read=False, expire_time=False, tag=False, retry=False):
52
"""
53
Retrieve value by key.
54
55
Args:
56
key (str): Cache key
57
default: Default value if key not found
58
version (int, optional): Key version for namespacing
59
read (bool): Return file handle instead of value. Default False.
60
expire_time (bool): Include expiration time in result. Default False.
61
tag (bool): Include tag in result. Default False.
62
retry (bool): Retry on timeout. Default False.
63
64
Returns:
65
Value, or tuple with additional info if expire_time/tag requested
66
"""
67
68
def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None, read=False, tag=None, retry=True):
69
"""
70
Store key-value pair.
71
72
Args:
73
key (str): Cache key
74
value: Value to store
75
timeout (int): Expiration timeout in seconds
76
version (int, optional): Key version for namespacing
77
read (bool): Store value as file for reading. Default False.
78
tag (str, optional): Tag for grouping related items
79
retry (bool): Retry on timeout. Default True.
80
81
Returns:
82
bool: True if set succeeded
83
"""
84
85
def delete(self, key, version=None, retry=True):
86
"""
87
Delete key from cache.
88
89
Args:
90
key (str): Cache key to delete
91
version (int, optional): Key version for namespacing
92
retry (bool): Retry on timeout. Default True.
93
94
Returns:
95
bool: True if key existed and was deleted
96
"""
97
98
def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None, retry=True):
99
"""
100
Update expiration time for existing key.
101
102
Args:
103
key (str): Cache key
104
timeout (int): New expiration timeout in seconds
105
version (int, optional): Key version for namespacing
106
retry (bool): Retry on timeout. Default True.
107
108
Returns:
109
bool: True if key existed and was touched
110
"""
111
112
def incr(self, key, delta=1, version=None, default=None, retry=True):
113
"""
114
Atomically increment numeric value.
115
116
Args:
117
key (str): Cache key
118
delta (int): Amount to increment. Default 1.
119
version (int, optional): Key version for namespacing
120
default (int, optional): Default value if key doesn't exist
121
retry (bool): Retry on timeout. Default True.
122
123
Returns:
124
New value after increment
125
126
Raises:
127
ValueError: If key doesn't exist and no default provided
128
"""
129
130
def decr(self, key, delta=1, version=None, default=None, retry=True):
131
"""
132
Atomically decrement numeric value.
133
134
Args:
135
key (str): Cache key
136
delta (int): Amount to decrement. Default 1.
137
version (int, optional): Key version for namespacing
138
default (int, optional): Default value if key doesn't exist
139
retry (bool): Retry on timeout. Default True.
140
141
Returns:
142
New value after decrement
143
144
Raises:
145
ValueError: If key doesn't exist and no default provided
146
"""
147
148
def has_key(self, key, version=None):
149
"""
150
Check if key exists in cache.
151
152
Args:
153
key (str): Cache key
154
version (int, optional): Key version for namespacing
155
156
Returns:
157
bool: True if key exists
158
"""
159
160
def clear(self):
161
"""
162
Remove all items from cache.
163
164
Returns:
165
None
166
"""
167
```
168
169
### DiskCache Extensions
170
171
Additional methods that extend Django's cache interface with DiskCache features.
172
173
```python { .api }
174
def read(self, key, version=None):
175
"""
176
Get file handle for key stored in read mode.
177
178
Args:
179
key (str): Cache key
180
version (int, optional): Key version for namespacing
181
182
Returns:
183
File handle or None if key not found
184
"""
185
186
def pop(self, key, default=None, version=None, expire_time=False, tag=False, retry=True):
187
"""
188
Remove and return value for key.
189
190
Args:
191
key (str): Cache key
192
default: Default value if key not found
193
version (int, optional): Key version for namespacing
194
expire_time (bool): Include expiration time in result. Default False.
195
tag (bool): Include tag in result. Default False.
196
retry (bool): Retry on timeout. Default True.
197
198
Returns:
199
Value, or tuple with additional info if expire_time/tag requested
200
"""
201
```
202
203
### Sub-collection Access
204
205
Create sub-collections within the Django cache directory.
206
207
```python { .api }
208
def cache(self, name):
209
"""
210
Return Cache instance in subdirectory.
211
212
Args:
213
name (str): Subdirectory name for Cache
214
215
Returns:
216
Cache: Cache instance in subdirectory
217
"""
218
219
def deque(self, name, maxlen=None):
220
"""
221
Return Deque instance in subdirectory.
222
223
Args:
224
name (str): Subdirectory name for Deque
225
maxlen (int, optional): Maximum length of deque
226
227
Returns:
228
Deque: Deque instance in subdirectory
229
"""
230
231
def index(self, name):
232
"""
233
Return Index instance in subdirectory.
234
235
Args:
236
name (str): Subdirectory name for Index
237
238
Returns:
239
Index: Index instance in subdirectory
240
"""
241
```
242
243
### Cache Management
244
245
Cache management operations with Django compatibility.
246
247
```python { .api }
248
def expire(self):
249
"""
250
Remove expired items from cache.
251
252
Returns:
253
int: Number of expired items removed
254
"""
255
256
def stats(self, enable=True, reset=False):
257
"""
258
Get cache hit/miss statistics.
259
260
Args:
261
enable (bool): Enable statistics tracking. Default True.
262
reset (bool): Reset statistics counters. Default False.
263
264
Returns:
265
Tuple of (hits, misses) counters
266
"""
267
268
def create_tag_index(self):
269
"""Create database index on tag column for faster tag operations."""
270
271
def drop_tag_index(self):
272
"""Drop database index on tag column."""
273
274
def evict(self, tag):
275
"""
276
Remove all items with specified tag.
277
278
Args:
279
tag (str): Tag to evict
280
281
Returns:
282
int: Number of items evicted
283
"""
284
285
def cull(self):
286
"""
287
Remove items according to eviction policy.
288
289
Returns:
290
int: Number of items removed
291
"""
292
293
def close(self, **kwargs):
294
"""
295
Close cache connections and cleanup resources.
296
297
Args:
298
**kwargs: Additional arguments (for Django compatibility)
299
"""
300
```
301
302
### Memoization
303
304
Django-compatible memoization decorator.
305
306
```python { .api }
307
def memoize(self, name=None, timeout=DEFAULT_TIMEOUT, version=None, typed=False, tag=None, ignore=()):
308
"""
309
Memoization decorator using this Django cache.
310
311
Args:
312
name (str, optional): Name for memoized function. Default function name.
313
timeout (int): Expiration timeout in seconds
314
version (int, optional): Key version for namespacing
315
typed (bool): Distinguish arguments by type. Default False.
316
tag (str, optional): Tag for grouping cached results
317
ignore (tuple): Argument positions/names to ignore in cache key
318
319
Returns:
320
Decorator function
321
"""
322
```
323
324
### Utility Methods
325
326
Utility methods for Django integration.
327
328
```python { .api }
329
def get_backend_timeout(self, timeout=DEFAULT_TIMEOUT):
330
"""
331
Convert Django timeout format to seconds.
332
333
Args:
334
timeout (int): Django timeout value
335
336
Returns:
337
float: Timeout in seconds, or None for no expiration
338
"""
339
```
340
341
## Django Configuration
342
343
### Settings Configuration
344
345
Configure DiskCache as a Django cache backend in your settings.py:
346
347
```python
348
# settings.py
349
CACHES = {
350
'default': {
351
'BACKEND': 'diskcache.djangocache.DjangoCache',
352
'LOCATION': '/tmp/django_cache',
353
'SHARDS': 8,
354
'DATABASE_TIMEOUT': 0.010,
355
'OPTIONS': {
356
'size_limit': 2**32, # 4GB
357
'eviction_policy': 'least-recently-used',
358
'statistics': 1,
359
'tag_index': 1,
360
},
361
},
362
'session': {
363
'BACKEND': 'diskcache.djangocache.DjangoCache',
364
'LOCATION': '/tmp/django_sessions',
365
'SHARDS': 4,
366
'OPTIONS': {
367
'size_limit': 2**28, # 256MB
368
'eviction_policy': 'least-recently-stored',
369
},
370
},
371
}
372
```
373
374
### Multiple Cache Configuration
375
376
```python
377
# Multiple caches for different purposes
378
CACHES = {
379
'default': {
380
'BACKEND': 'diskcache.djangocache.DjangoCache',
381
'LOCATION': '/var/cache/django/default',
382
'SHARDS': 16,
383
'OPTIONS': {
384
'size_limit': 2**30, # 1GB
385
'eviction_policy': 'least-recently-used',
386
'statistics': 1,
387
},
388
},
389
'sessions': {
390
'BACKEND': 'diskcache.djangocache.DjangoCache',
391
'LOCATION': '/var/cache/django/sessions',
392
'SHARDS': 4,
393
'OPTIONS': {
394
'size_limit': 2**28, # 256MB
395
'eviction_policy': 'least-recently-stored',
396
},
397
},
398
'database': {
399
'BACKEND': 'diskcache.djangocache.DjangoCache',
400
'LOCATION': '/var/cache/django/database',
401
'SHARDS': 32,
402
'OPTIONS': {
403
'size_limit': 2**32, # 4GB
404
'eviction_policy': 'least-frequently-used',
405
'statistics': 1,
406
'tag_index': 1,
407
},
408
},
409
}
410
```
411
412
## Usage Examples
413
414
### Basic Django Cache Usage
415
416
```python
417
from django.core.cache import cache
418
419
# Basic cache operations
420
cache.set('user_count', 1250, timeout=300) # 5 minutes
421
user_count = cache.get('user_count', default=0)
422
423
# Add only if doesn't exist
424
cache.add('unique_visitor', True, timeout=86400) # 24 hours
425
426
# Atomic operations
427
cache.set('page_views', 1000)
428
cache.incr('page_views') # Now 1001
429
cache.decr('page_views', 10) # Now 991
430
431
# Delete operations
432
cache.delete('temp_data')
433
cache.clear() # Remove all items
434
```
435
436
### Django Cache with Versioning
437
438
```python
439
from django.core.cache import cache
440
441
# Use versioning for cache invalidation
442
cache.set('user_profile', user_data, version=1)
443
profile = cache.get('user_profile', version=1)
444
445
# Invalidate by changing version
446
cache.set('user_profile', updated_data, version=2)
447
old_profile = cache.get('user_profile', version=1) # None
448
new_profile = cache.get('user_profile', version=2) # updated_data
449
```
450
451
### Using Multiple Caches
452
453
```python
454
from django.core.cache import caches
455
456
# Access different cache backends
457
default_cache = caches['default']
458
session_cache = caches['sessions']
459
db_cache = caches['database']
460
461
# Use each for different purposes
462
default_cache.set('site_config', config_data)
463
session_cache.set('user_session', session_data, timeout=1800)
464
db_cache.set('query_result', query_data, timeout=3600)
465
```
466
467
### DiskCache-specific Features
468
469
```python
470
from django.core.cache import cache
471
472
# Use tags for grouped invalidation
473
cache.set('post_1', post_data, tag='posts', timeout=3600)
474
cache.set('post_2', other_post, tag='posts', timeout=3600)
475
cache.set('user_1', user_data, tag='users', timeout=1800)
476
477
# Evict all posts
478
cache.evict('posts') # Removes post_1 and post_2, keeps user_1
479
480
# File-based storage for large items
481
with open('large_file.pdf', 'rb') as f:
482
file_data = f.read()
483
cache.set('large_document', file_data, read=True)
484
485
# Get file handle instead of loading into memory
486
file_handle = cache.read('large_document')
487
if file_handle:
488
content = file_handle.read()
489
file_handle.close()
490
491
# Statistics
492
hits, misses = cache.stats()
493
print(f"Cache hit ratio: {hits/(hits+misses):.2%}")
494
```
495
496
### Sub-collections in Django
497
498
```python
499
from django.core.cache import cache
500
501
# Create specialized data structures
502
user_cache = cache.cache('users')
503
task_queue = cache.deque('tasks')
504
search_index = cache.index('search')
505
506
# Use sub-collections independently
507
user_cache.set('user:123', user_data)
508
task_queue.append({'task': 'send_email', 'user_id': 123})
509
search_index['keyword:python'] = ['doc1', 'doc2', 'doc3']
510
511
# These don't interfere with main cache
512
cache.set('global_setting', 'value') # Different namespace
513
```
514
515
### Session Backend
516
517
```python
518
# Use DiskCache as Django session backend
519
# In settings.py:
520
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
521
SESSION_CACHE_ALIAS = 'sessions'
522
523
# Sessions will be stored in DiskCache automatically
524
# with all the persistence and performance benefits
525
```
526
527
### Caching Views and Templates
528
529
```python
530
from django.views.decorators.cache import cache_page
531
from django.core.cache import cache
532
from django.template.loader import render_to_string
533
534
# Cache entire view for 15 minutes
535
@cache_page(60 * 15, cache='default')
536
def expensive_view(request):
537
# Expensive computation
538
data = perform_complex_calculation()
539
return render(request, 'template.html', {'data': data})
540
541
# Manual template caching
542
def cached_template_fragment(context_data):
543
cache_key = f"template_fragment_{hash(str(context_data))}"
544
rendered = cache.get(cache_key)
545
546
if rendered is None:
547
rendered = render_to_string('fragment.html', context_data)
548
cache.set(cache_key, rendered, timeout=3600, tag='templates')
549
550
return rendered
551
552
# Invalidate template cache when data changes
553
def invalidate_template_cache():
554
cache.evict('templates') # Remove all cached templates
555
```
556
557
### Database Query Caching
558
559
```python
560
from django.core.cache import cache
561
from django.db import models
562
from django.utils.hashlib import md5_constructor
563
564
class CachedQueryManager(models.Manager):
565
def cached_filter(self, timeout=3600, **kwargs):
566
# Create cache key from query parameters
567
key_data = str(sorted(kwargs.items()))
568
cache_key = f"query_{self.model._meta.label}_{md5_constructor(key_data.encode()).hexdigest()}"
569
570
# Try cache first
571
result = cache.get(cache_key)
572
if result is None:
573
result = list(self.filter(**kwargs))
574
cache.set(cache_key, result, timeout=timeout, tag=f'model_{self.model._meta.label}')
575
576
return result
577
578
class Article(models.Model):
579
title = models.CharField(max_length=200)
580
content = models.TextField()
581
published = models.BooleanField(default=False)
582
583
objects = CachedQueryManager()
584
585
# Usage
586
published_articles = Article.objects.cached_filter(published=True, timeout=1800)
587
588
# Invalidate when models change
589
def invalidate_article_cache():
590
cache.evict('model_myapp.Article')
591
```
592
593
### Memoization in Django
594
595
```python
596
from django.core.cache import cache
597
598
# Memoize expensive functions
599
@cache.memoize(timeout=3600, tag='calculations')
600
def expensive_calculation(x, y, z):
601
# Complex computation
602
result = perform_expensive_operation(x, y, z)
603
return result
604
605
# Memoize with versioning
606
@cache.memoize(name='user_permissions', timeout=1800, version=1)
607
def get_user_permissions(user_id):
608
# Database queries to get permissions
609
return User.objects.get(id=user_id).get_all_permissions()
610
611
# Use in views
612
def user_dashboard(request):
613
permissions = get_user_permissions(request.user.id)
614
calculation_result = expensive_calculation(1, 2, 3)
615
616
return render(request, 'dashboard.html', {
617
'permissions': permissions,
618
'result': calculation_result
619
})
620
621
# Invalidate memoized functions
622
def invalidate_calculations():
623
cache.evict('calculations')
624
```
625
626
## Best Practices
627
628
### Cache Key Management
629
630
```python
631
# Use consistent key naming
632
CACHE_KEY_PATTERNS = {
633
'user_profile': 'user:profile:{user_id}',
634
'article_list': 'articles:list:{category}:{page}',
635
'search_results': 'search:{query_hash}',
636
}
637
638
def get_cache_key(pattern, **kwargs):
639
return CACHE_KEY_PATTERNS[pattern].format(**kwargs)
640
641
# Usage
642
cache_key = get_cache_key('user_profile', user_id=123)
643
cache.set(cache_key, user_data)
644
```
645
646
### Timeout Strategies
647
648
```python
649
# Different timeouts for different data types
650
CACHE_TIMEOUTS = {
651
'static_content': 86400, # 24 hours
652
'user_sessions': 1800, # 30 minutes
653
'api_responses': 300, # 5 minutes
654
'database_queries': 3600, # 1 hour
655
'template_fragments': 7200, # 2 hours
656
}
657
658
cache.set('config', data, timeout=CACHE_TIMEOUTS['static_content'])
659
```
660
661
### Error Handling
662
663
```python
664
from django.core.cache import cache
665
import logging
666
667
def safe_cache_get(key, default=None):
668
try:
669
return cache.get(key, default)
670
except Exception as e:
671
logging.error(f"Cache get error for key {key}: {e}")
672
return default
673
674
def safe_cache_set(key, value, timeout=300):
675
try:
676
return cache.set(key, value, timeout=timeout)
677
except Exception as e:
678
logging.error(f"Cache set error for key {key}: {e}")
679
return False
680
```
681
682
### Monitoring and Maintenance
683
684
```python
685
# Management command for cache monitoring
686
from django.core.management.base import BaseCommand
687
from django.core.cache import cache
688
689
class Command(BaseCommand):
690
help = 'Monitor cache statistics'
691
692
def handle(self, *args, **options):
693
hits, misses = cache.stats()
694
total = hits + misses
695
696
if total > 0:
697
hit_ratio = hits / total
698
self.stdout.write(f"Cache Statistics:")
699
self.stdout.write(f" Hits: {hits}")
700
self.stdout.write(f" Misses: {misses}")
701
self.stdout.write(f" Hit Ratio: {hit_ratio:.2%}")
702
703
# Cleanup expired items
704
expired = cache.expire()
705
self.stdout.write(f"Expired {expired} items")
706
707
# Show cache size
708
volume = cache.volume() if hasattr(cache, 'volume') else 'Unknown'
709
self.stdout.write(f"Cache Size: {volume} bytes")
710
```