or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

access-control.mdconfiguration.mddjango-signals.mdfile-operations.mdindex.mdstorage-backends.mdtemplate-integration.mdtranslation-services.mdweb-interface.md

storage-backends.mddocs/

0

# Storage Backends

1

2

Pluggable storage system for persisting translation work-in-progress data, supporting session-based, cache-based, or custom storage implementations. Storage backends handle temporary data during translation sessions, including unsaved changes, filters, and pagination state.

3

4

## Capabilities

5

6

### Base Storage Interface

7

8

Abstract base class defining the storage backend interface that all implementations must follow.

9

10

```python { .api }

11

class BaseRosettaStorage:

12

"""

13

Abstract base class for storage backends.

14

15

Defines the interface that all Rosetta storage backends must implement

16

for persisting translation session data.

17

"""

18

19

def __init__(self, request):

20

"""

21

Initialize storage backend with request context.

22

23

Parameters:

24

- request: Django HttpRequest instance

25

"""

26

27

def set(self, key: str, value) -> None:

28

"""

29

Store a value with the given key.

30

31

Parameters:

32

- key: Storage key string

33

- value: Value to store (must be serializable)

34

"""

35

raise NotImplementedError

36

37

def get(self, key: str, default=None):

38

"""

39

Retrieve a value by key.

40

41

Parameters:

42

- key: Storage key string

43

- default: Default value if key not found

44

45

Returns:

46

Stored value or default

47

"""

48

raise NotImplementedError

49

50

def has(self, key: str) -> bool:

51

"""

52

Check if key exists in storage.

53

54

Parameters:

55

- key: Storage key string

56

57

Returns:

58

Boolean indicating key existence

59

"""

60

raise NotImplementedError

61

62

def delete(self, key: str) -> None:

63

"""

64

Delete a key from storage.

65

66

Parameters:

67

- key: Storage key string to delete

68

"""

69

raise NotImplementedError

70

```

71

72

### Session Storage Backend

73

74

Session-based storage implementation using Django's session framework.

75

76

```python { .api }

77

class SessionRosettaStorage(BaseRosettaStorage):

78

"""

79

Session-based storage backend.

80

81

Stores translation data in Django sessions, persisting across

82

browser sessions but limited to single user/browser. Good for

83

development and single-user deployments.

84

"""

85

86

def __init__(self, request):

87

"""Initialize with request session."""

88

self.request = request

89

90

def set(self, key: str, value) -> None:

91

"""Store value in session."""

92

93

def get(self, key: str, default=None):

94

"""Retrieve value from session."""

95

96

def has(self, key: str) -> bool:

97

"""Check if key exists in session."""

98

99

def delete(self, key: str) -> None:

100

"""Delete key from session."""

101

```

102

103

### Cache Storage Backend

104

105

Cache-based storage implementation using Django's caching framework (default).

106

107

```python { .api }

108

class CacheRosettaStorage(BaseRosettaStorage):

109

"""

110

Cache-based storage backend (default).

111

112

Uses Django's caching framework for storage, supporting multiple

113

cache backends (Redis, Memcached, database, etc.). Recommended

114

for production deployments with multiple users.

115

"""

116

117

def __init__(self, request):

118

"""Initialize with cache instance."""

119

self.request = request

120

self.cache = caches[rosetta_settings.CACHE_NAME]

121

122

def set(self, key: str, value) -> None:

123

"""Store value in cache."""

124

125

def get(self, key: str, default=None):

126

"""Retrieve value from cache."""

127

128

def has(self, key: str) -> bool:

129

"""Check if key exists in cache."""

130

131

def delete(self, key: str) -> None:

132

"""Delete key from cache."""

133

```

134

135

### Dummy Storage Backend

136

137

No-operation storage implementation for testing or minimal setups.

138

139

```python { .api }

140

class DummyRosettaStorage(BaseRosettaStorage):

141

"""

142

No-operation storage backend.

143

144

Provides storage interface but doesn't actually persist data.

145

Useful for testing or environments where persistence isn't needed.

146

"""

147

148

def set(self, key: str, value) -> None:

149

"""No-op set operation."""

150

pass

151

152

def get(self, key: str, default=None):

153

"""Always returns default value."""

154

return default

155

156

def has(self, key: str) -> bool:

157

"""Always returns False."""

158

return False

159

160

def delete(self, key: str) -> None:

161

"""No-op delete operation."""

162

pass

163

```

164

165

### Storage Factory

166

167

Factory function for creating storage backend instances.

168

169

```python { .api }

170

def get_storage(request) -> BaseRosettaStorage:

171

"""

172

Get configured storage instance for request.

173

174

Creates and returns storage backend instance based on

175

ROSETTA_STORAGE_CLASS setting.

176

177

Parameters:

178

- request: Django HttpRequest instance

179

180

Returns:

181

Storage backend instance implementing BaseRosettaStorage

182

183

Raises:

184

ImportError: If configured storage class cannot be imported

185

"""

186

187

# Cache instance for storage operations

188

cache = caches[rosetta_settings.CACHE_NAME]

189

"""Django cache instance used by cache storage backend."""

190

```

191

192

## Configuration

193

194

Storage backends are configured through Django settings:

195

196

```python { .api }

197

ROSETTA_STORAGE_CLASS: str = 'rosetta.storage.CacheRosettaStorage'

198

"""

199

Full Python path to storage backend class.

200

Available options:

201

- 'rosetta.storage.CacheRosettaStorage' (default, recommended)

202

- 'rosetta.storage.SessionRosettaStorage'

203

- 'rosetta.storage.DummyRosettaStorage'

204

- Custom implementation path

205

"""

206

207

ROSETTA_CACHE_NAME: str = 'default'

208

"""

209

Name of Django cache backend to use with CacheRosettaStorage.

210

Must correspond to a cache defined in CACHES setting.

211

"""

212

```

213

214

## Usage Examples

215

216

### Basic Storage Configuration

217

218

```python

219

# settings.py - Use default cache storage (recommended)

220

ROSETTA_STORAGE_CLASS = 'rosetta.storage.CacheRosettaStorage'

221

ROSETTA_CACHE_NAME = 'default'

222

223

# Ensure you have a cache configured

224

CACHES = {

225

'default': {

226

'BACKEND': 'django.core.cache.backends.redis.RedisCache',

227

'LOCATION': 'redis://127.0.0.1:6379/1',

228

}

229

}

230

```

231

232

### Session Storage Configuration

233

234

```python

235

# settings.py - Use session storage for development

236

ROSETTA_STORAGE_CLASS = 'rosetta.storage.SessionRosettaStorage'

237

238

# Ensure sessions are properly configured

239

MIDDLEWARE = [

240

'django.contrib.sessions.middleware.SessionMiddleware',

241

# ... other middleware

242

]

243

244

INSTALLED_APPS = [

245

'django.contrib.sessions',

246

# ... other apps

247

]

248

```

249

250

### Custom Cache Backend

251

252

```python

253

# settings.py - Use dedicated cache for Rosetta

254

CACHES = {

255

'default': {

256

'BACKEND': 'django.core.cache.backends.redis.RedisCache',

257

'LOCATION': 'redis://127.0.0.1:6379/1',

258

},

259

'rosetta': {

260

'BACKEND': 'django.core.cache.backends.redis.RedisCache',

261

'LOCATION': 'redis://127.0.0.1:6379/2',

262

'TIMEOUT': 3600, # 1 hour timeout for translation sessions

263

}

264

}

265

266

ROSETTA_STORAGE_CLASS = 'rosetta.storage.CacheRosettaStorage'

267

ROSETTA_CACHE_NAME = 'rosetta'

268

```

269

270

### Custom Storage Backend Implementation

271

272

```python

273

# myapp/storage.py - Custom storage backend

274

from rosetta.storage import BaseRosettaStorage

275

import json

276

import os

277

278

class FileRosettaStorage(BaseRosettaStorage):

279

"""File-based storage backend for Rosetta."""

280

281

def __init__(self, request):

282

self.request = request

283

self.user_id = request.user.id if request.user.is_authenticated else 'anonymous'

284

self.storage_dir = f'/tmp/rosetta_storage/{self.user_id}'

285

os.makedirs(self.storage_dir, exist_ok=True)

286

287

def _get_file_path(self, key):

288

return os.path.join(self.storage_dir, f'{key}.json')

289

290

def set(self, key, value):

291

file_path = self._get_file_path(key)

292

with open(file_path, 'w') as f:

293

json.dump(value, f)

294

295

def get(self, key, default=None):

296

file_path = self._get_file_path(key)

297

try:

298

with open(file_path, 'r') as f:

299

return json.load(f)

300

except (FileNotFoundError, json.JSONDecodeError):

301

return default

302

303

def has(self, key):

304

return os.path.exists(self._get_file_path(key))

305

306

def delete(self, key):

307

file_path = self._get_file_path(key)

308

try:

309

os.remove(file_path)

310

except FileNotFoundError:

311

pass

312

313

# settings.py

314

ROSETTA_STORAGE_CLASS = 'myapp.storage.FileRosettaStorage'

315

```

316

317

### Programmatic Storage Usage

318

319

```python

320

from rosetta.storage import get_storage

321

322

def my_view(request):

323

"""Example of programmatic storage usage."""

324

325

# Get storage instance for request

326

storage = get_storage(request)

327

328

# Store translation session data

329

storage.set('current_filter', {

330

'language': 'fr',

331

'status': 'untranslated'

332

})

333

334

# Retrieve stored data

335

filter_data = storage.get('current_filter', {})

336

337

# Check if key exists

338

if storage.has('user_preferences'):

339

preferences = storage.get('user_preferences')

340

341

# Delete temporary data

342

storage.delete('temp_data')

343

344

return render(request, 'template.html')

345

```

346

347

### Database Storage Backend

348

349

```python

350

# myapp/storage.py - Database-backed storage

351

from rosetta.storage import BaseRosettaStorage

352

from django.core.cache import cache

353

from myapp.models import RosettaSessionData

354

355

class DatabaseRosettaStorage(BaseRosettaStorage):

356

"""Database-backed storage for persistent sessions."""

357

358

def __init__(self, request):

359

self.request = request

360

self.user_id = request.user.id if request.user.is_authenticated else None

361

self.session_key = request.session.session_key

362

363

def _get_session_data(self):

364

"""Get or create session data object."""

365

if self.user_id:

366

obj, created = RosettaSessionData.objects.get_or_create(

367

user_id=self.user_id,

368

defaults={'data': {}}

369

)

370

else:

371

obj, created = RosettaSessionData.objects.get_or_create(

372

session_key=self.session_key,

373

defaults={'data': {}}

374

)

375

return obj

376

377

def set(self, key, value):

378

obj = self._get_session_data()

379

obj.data[key] = value

380

obj.save()

381

382

def get(self, key, default=None):

383

obj = self._get_session_data()

384

return obj.data.get(key, default)

385

386

def has(self, key):

387

obj = self._get_session_data()

388

return key in obj.data

389

390

def delete(self, key):

391

obj = self._get_session_data()

392

obj.data.pop(key, None)

393

obj.save()

394

395

# myapp/models.py

396

from django.db import models

397

from django.contrib.auth.models import User

398

399

class RosettaSessionData(models.Model):

400

user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)

401

session_key = models.CharField(max_length=40, null=True, blank=True)

402

data = models.JSONField(default=dict)

403

created_at = models.DateTimeField(auto_now_add=True)

404

updated_at = models.DateTimeField(auto_now=True)

405

406

class Meta:

407

unique_together = [['user'], ['session_key']]

408

```

409

410

### Testing Storage Backends

411

412

```python

413

# tests.py - Testing storage implementations

414

from django.test import TestCase, RequestFactory

415

from django.contrib.auth.models import User

416

from rosetta.storage import get_storage, CacheRosettaStorage, SessionRosettaStorage

417

418

class StorageBackendTests(TestCase):

419

def setUp(self):

420

self.factory = RequestFactory()

421

self.user = User.objects.create_user('testuser', 'test@example.com', 'pass')

422

423

def test_cache_storage(self):

424

"""Test cache storage backend."""

425

request = self.factory.get('/')

426

request.user = self.user

427

428

storage = CacheRosettaStorage(request)

429

430

# Test set/get

431

storage.set('test_key', 'test_value')

432

self.assertEqual(storage.get('test_key'), 'test_value')

433

434

# Test has

435

self.assertTrue(storage.has('test_key'))

436

self.assertFalse(storage.has('nonexistent_key'))

437

438

# Test delete

439

storage.delete('test_key')

440

self.assertFalse(storage.has('test_key'))

441

442

def test_session_storage(self):

443

"""Test session storage backend."""

444

request = self.factory.get('/')

445

request.user = self.user

446

request.session = {}

447

448

storage = SessionRosettaStorage(request)

449

450

# Test operations

451

storage.set('session_key', {'data': 'value'})

452

self.assertEqual(storage.get('session_key'), {'data': 'value'})

453

454

# Test default value

455

self.assertEqual(storage.get('missing_key', 'default'), 'default')

456

```