or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-caching.mddisk-serialization.mddjango-integration.mdfanout-cache.mdindex.mdpersistent-data-structures.mdrecipe-functions.mdsynchronization-primitives.md

django-integration.mddocs/

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

```