A Django application that facilitates the translation process of your Django projects
—
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.
Abstract base class defining the storage backend interface that all implementations must follow.
class BaseRosettaStorage:
"""
Abstract base class for storage backends.
Defines the interface that all Rosetta storage backends must implement
for persisting translation session data.
"""
def __init__(self, request):
"""
Initialize storage backend with request context.
Parameters:
- request: Django HttpRequest instance
"""
def set(self, key: str, value) -> None:
"""
Store a value with the given key.
Parameters:
- key: Storage key string
- value: Value to store (must be serializable)
"""
raise NotImplementedError
def get(self, key: str, default=None):
"""
Retrieve a value by key.
Parameters:
- key: Storage key string
- default: Default value if key not found
Returns:
Stored value or default
"""
raise NotImplementedError
def has(self, key: str) -> bool:
"""
Check if key exists in storage.
Parameters:
- key: Storage key string
Returns:
Boolean indicating key existence
"""
raise NotImplementedError
def delete(self, key: str) -> None:
"""
Delete a key from storage.
Parameters:
- key: Storage key string to delete
"""
raise NotImplementedErrorSession-based storage implementation using Django's session framework.
class SessionRosettaStorage(BaseRosettaStorage):
"""
Session-based storage backend.
Stores translation data in Django sessions, persisting across
browser sessions but limited to single user/browser. Good for
development and single-user deployments.
"""
def __init__(self, request):
"""Initialize with request session."""
self.request = request
def set(self, key: str, value) -> None:
"""Store value in session."""
def get(self, key: str, default=None):
"""Retrieve value from session."""
def has(self, key: str) -> bool:
"""Check if key exists in session."""
def delete(self, key: str) -> None:
"""Delete key from session."""Cache-based storage implementation using Django's caching framework (default).
class CacheRosettaStorage(BaseRosettaStorage):
"""
Cache-based storage backend (default).
Uses Django's caching framework for storage, supporting multiple
cache backends (Redis, Memcached, database, etc.). Recommended
for production deployments with multiple users.
"""
def __init__(self, request):
"""Initialize with cache instance."""
self.request = request
self.cache = caches[rosetta_settings.CACHE_NAME]
def set(self, key: str, value) -> None:
"""Store value in cache."""
def get(self, key: str, default=None):
"""Retrieve value from cache."""
def has(self, key: str) -> bool:
"""Check if key exists in cache."""
def delete(self, key: str) -> None:
"""Delete key from cache."""No-operation storage implementation for testing or minimal setups.
class DummyRosettaStorage(BaseRosettaStorage):
"""
No-operation storage backend.
Provides storage interface but doesn't actually persist data.
Useful for testing or environments where persistence isn't needed.
"""
def set(self, key: str, value) -> None:
"""No-op set operation."""
pass
def get(self, key: str, default=None):
"""Always returns default value."""
return default
def has(self, key: str) -> bool:
"""Always returns False."""
return False
def delete(self, key: str) -> None:
"""No-op delete operation."""
passFactory function for creating storage backend instances.
def get_storage(request) -> BaseRosettaStorage:
"""
Get configured storage instance for request.
Creates and returns storage backend instance based on
ROSETTA_STORAGE_CLASS setting.
Parameters:
- request: Django HttpRequest instance
Returns:
Storage backend instance implementing BaseRosettaStorage
Raises:
ImportError: If configured storage class cannot be imported
"""
# Cache instance for storage operations
cache = caches[rosetta_settings.CACHE_NAME]
"""Django cache instance used by cache storage backend."""Storage backends are configured through Django settings:
ROSETTA_STORAGE_CLASS: str = 'rosetta.storage.CacheRosettaStorage'
"""
Full Python path to storage backend class.
Available options:
- 'rosetta.storage.CacheRosettaStorage' (default, recommended)
- 'rosetta.storage.SessionRosettaStorage'
- 'rosetta.storage.DummyRosettaStorage'
- Custom implementation path
"""
ROSETTA_CACHE_NAME: str = 'default'
"""
Name of Django cache backend to use with CacheRosettaStorage.
Must correspond to a cache defined in CACHES setting.
"""# settings.py - Use default cache storage (recommended)
ROSETTA_STORAGE_CLASS = 'rosetta.storage.CacheRosettaStorage'
ROSETTA_CACHE_NAME = 'default'
# Ensure you have a cache configured
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
}
}# settings.py - Use session storage for development
ROSETTA_STORAGE_CLASS = 'rosetta.storage.SessionRosettaStorage'
# Ensure sessions are properly configured
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
# ... other middleware
]
INSTALLED_APPS = [
'django.contrib.sessions',
# ... other apps
]# settings.py - Use dedicated cache for Rosetta
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
},
'rosetta': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/2',
'TIMEOUT': 3600, # 1 hour timeout for translation sessions
}
}
ROSETTA_STORAGE_CLASS = 'rosetta.storage.CacheRosettaStorage'
ROSETTA_CACHE_NAME = 'rosetta'# myapp/storage.py - Custom storage backend
from rosetta.storage import BaseRosettaStorage
import json
import os
class FileRosettaStorage(BaseRosettaStorage):
"""File-based storage backend for Rosetta."""
def __init__(self, request):
self.request = request
self.user_id = request.user.id if request.user.is_authenticated else 'anonymous'
self.storage_dir = f'/tmp/rosetta_storage/{self.user_id}'
os.makedirs(self.storage_dir, exist_ok=True)
def _get_file_path(self, key):
return os.path.join(self.storage_dir, f'{key}.json')
def set(self, key, value):
file_path = self._get_file_path(key)
with open(file_path, 'w') as f:
json.dump(value, f)
def get(self, key, default=None):
file_path = self._get_file_path(key)
try:
with open(file_path, 'r') as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return default
def has(self, key):
return os.path.exists(self._get_file_path(key))
def delete(self, key):
file_path = self._get_file_path(key)
try:
os.remove(file_path)
except FileNotFoundError:
pass
# settings.py
ROSETTA_STORAGE_CLASS = 'myapp.storage.FileRosettaStorage'from rosetta.storage import get_storage
def my_view(request):
"""Example of programmatic storage usage."""
# Get storage instance for request
storage = get_storage(request)
# Store translation session data
storage.set('current_filter', {
'language': 'fr',
'status': 'untranslated'
})
# Retrieve stored data
filter_data = storage.get('current_filter', {})
# Check if key exists
if storage.has('user_preferences'):
preferences = storage.get('user_preferences')
# Delete temporary data
storage.delete('temp_data')
return render(request, 'template.html')# myapp/storage.py - Database-backed storage
from rosetta.storage import BaseRosettaStorage
from django.core.cache import cache
from myapp.models import RosettaSessionData
class DatabaseRosettaStorage(BaseRosettaStorage):
"""Database-backed storage for persistent sessions."""
def __init__(self, request):
self.request = request
self.user_id = request.user.id if request.user.is_authenticated else None
self.session_key = request.session.session_key
def _get_session_data(self):
"""Get or create session data object."""
if self.user_id:
obj, created = RosettaSessionData.objects.get_or_create(
user_id=self.user_id,
defaults={'data': {}}
)
else:
obj, created = RosettaSessionData.objects.get_or_create(
session_key=self.session_key,
defaults={'data': {}}
)
return obj
def set(self, key, value):
obj = self._get_session_data()
obj.data[key] = value
obj.save()
def get(self, key, default=None):
obj = self._get_session_data()
return obj.data.get(key, default)
def has(self, key):
obj = self._get_session_data()
return key in obj.data
def delete(self, key):
obj = self._get_session_data()
obj.data.pop(key, None)
obj.save()
# myapp/models.py
from django.db import models
from django.contrib.auth.models import User
class RosettaSessionData(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
session_key = models.CharField(max_length=40, null=True, blank=True)
data = models.JSONField(default=dict)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = [['user'], ['session_key']]# tests.py - Testing storage implementations
from django.test import TestCase, RequestFactory
from django.contrib.auth.models import User
from rosetta.storage import get_storage, CacheRosettaStorage, SessionRosettaStorage
class StorageBackendTests(TestCase):
def setUp(self):
self.factory = RequestFactory()
self.user = User.objects.create_user('testuser', 'test@example.com', 'pass')
def test_cache_storage(self):
"""Test cache storage backend."""
request = self.factory.get('/')
request.user = self.user
storage = CacheRosettaStorage(request)
# Test set/get
storage.set('test_key', 'test_value')
self.assertEqual(storage.get('test_key'), 'test_value')
# Test has
self.assertTrue(storage.has('test_key'))
self.assertFalse(storage.has('nonexistent_key'))
# Test delete
storage.delete('test_key')
self.assertFalse(storage.has('test_key'))
def test_session_storage(self):
"""Test session storage backend."""
request = self.factory.get('/')
request.user = self.user
request.session = {}
storage = SessionRosettaStorage(request)
# Test operations
storage.set('session_key', {'data': 'value'})
self.assertEqual(storage.get('session_key'), {'data': 'value'})
# Test default value
self.assertEqual(storage.get('missing_key', 'default'), 'default')Install with Tessl CLI
npx tessl i tessl/pypi-django-rosetta