Dynamic global and instance settings for your django project
—
Value serialization system for database storage with support for complex Python objects and custom serialization logic. This system handles the conversion between Python objects and database-storable string representations.
Foundation classes for implementing serialization logic with support for both class-based and instance-based serializers.
class BaseSerializer:
"""
Base serializer for converting Python variables to database strings.
Provides the foundation for all serialization operations with
class-based methods for stateless serialization.
"""
@classmethod
def serialize(cls, value, **kwargs) -> str:
"""
Convert Python value to database string representation.
Args:
- value: Python value to serialize
- **kwargs: Additional serialization options
Returns:
String representation suitable for database storage
Raises:
- SerializationError: If serialization fails
"""
@classmethod
def deserialize(cls, value, **kwargs):
"""
Convert database string back to Python value.
Args:
- value: String value from database
- **kwargs: Additional deserialization options
Returns:
Python value
Raises:
- SerializationError: If deserialization fails
"""
@classmethod
def to_python(cls, value, **kwargs):
"""
Abstract method for converting string to Python value.
Must be implemented by subclasses.
Args:
- value: String value to convert
- **kwargs: Additional conversion options
Returns:
Python value
"""
@classmethod
def to_db(cls, value, **kwargs) -> str:
"""
Convert Python value to database string.
Args:
- value: Python value to convert
- **kwargs: Additional conversion options
Returns:
Database-safe string representation
"""
@classmethod
def clean_to_db_value(cls, value):
"""
Clean and validate value before database storage.
Args:
- value: Value to clean
Returns:
Cleaned value ready for serialization
"""
class InstanciatedSerializer(BaseSerializer):
"""
Instance-based serializer for cases requiring additional context.
Used when serialization requires additional data or state
that cannot be handled by class methods alone.
"""
def __init__(self, **kwargs):
"""
Initialize serializer with context data.
Args:
- **kwargs: Context data for serialization
"""Serializers for fundamental Python data types with proper type conversion and validation.
class BooleanSerializer(BaseSerializer):
"""
Serialize boolean values to/from database strings.
Handles: True/False -> "True"/"False"
"""
@classmethod
def to_python(cls, value) -> bool:
"""Convert string to boolean."""
@classmethod
def to_db(cls, value) -> str:
"""Convert boolean to string."""
class IntegerSerializer(BaseSerializer):
"""
Serialize integer values to/from database strings.
Handles: 42 -> "42"
Alias: IntSerializer
"""
@classmethod
def to_python(cls, value) -> int:
"""Convert string to integer."""
@classmethod
def to_db(cls, value) -> str:
"""Convert integer to string."""
# Alias for backward compatibility
IntSerializer = IntegerSerializer
class DecimalSerializer(BaseSerializer):
"""
Serialize Decimal values to/from database strings.
Handles: Decimal('123.45') -> "123.45"
Preserves precision for financial/scientific calculations.
"""
@classmethod
def to_python(cls, value):
"""Convert string to Decimal."""
@classmethod
def to_db(cls, value) -> str:
"""Convert Decimal to string."""
class FloatSerializer(BaseSerializer):
"""
Serialize float values to/from database strings.
Handles: 3.14159 -> "3.14159"
"""
@classmethod
def to_python(cls, value) -> float:
"""Convert string to float."""
@classmethod
def to_db(cls, value) -> str:
"""Convert float to string."""
class StringSerializer(BaseSerializer):
"""
Serialize string values with optional HTML escaping.
Handles: "Hello World" -> "Hello World"
Supports HTML escaping for security.
"""
@classmethod
def to_python(cls, value) -> str:
"""Convert database string to Python string."""
@classmethod
def to_db(cls, value) -> str:
"""Convert Python string to database string."""Serializers for handling multiple values and complex data structures.
class MultipleSerializer(BaseSerializer):
"""
Serialize multiple choice values as JSON arrays.
Handles: ['choice1', 'choice2'] -> '["choice1", "choice2"]'
"""
@classmethod
def to_python(cls, value) -> list:
"""Convert JSON string to Python list."""
@classmethod
def to_db(cls, value) -> str:
"""Convert Python list to JSON string."""Serializers for handling Django model instances and relationships.
class ModelSerializer(InstanciatedSerializer):
"""
Serialize Django model instances by primary key.
Handles model instance -> PK string -> model instance
Requires model class context for deserialization.
"""
def __init__(self, model_class=None, **kwargs):
"""
Initialize with model class for lookups.
Args:
- model_class: Django model class
- **kwargs: Additional context
"""
self.model_class = model_class
super().__init__(**kwargs)
def to_python(self, value):
"""
Convert primary key to model instance.
Args:
- value: Primary key value
Returns:
Model instance or None
"""
def to_db(self, value) -> str:
"""
Convert model instance to primary key string.
Args:
- value: Model instance
Returns:
Primary key as string
"""
class ModelMultipleSerializer(ModelSerializer):
"""
Serialize multiple Django model instances.
Handles: [model1, model2] -> "[pk1, pk2]" -> [model1, model2]
"""
def to_python(self, value) -> list:
"""Convert PK list to model instance list."""
def to_db(self, value) -> str:
"""Convert model instance list to PK JSON string."""Serializers for handling file uploads and file references.
class FileSerializer(InstanciatedSerializer):
"""
Serialize file uploads with proper file handling.
Handles file storage, path management, and cleanup.
"""
def __init__(self, upload_path=None, storage=None, **kwargs):
"""
Initialize with file handling configuration.
Args:
- upload_path: Path for file uploads
- storage: Django storage backend
- **kwargs: Additional context
"""
self.upload_path = upload_path
self.storage = storage
super().__init__(**kwargs)
def to_python(self, value):
"""
Convert file path to file object.
Args:
- value: File path string
Returns:
File object or None
"""
def to_db(self, value) -> str:
"""
Store file and return path string.
Args:
- value: File object
Returns:
File path as string
"""
class PreferenceFieldFile:
"""
FieldFile subclass for preference files.
Provides file-like interface for preference file handling
with proper integration with Django's file storage system.
"""
def __init__(self, instance, field, name):
"""
Initialize preference field file.
Args:
- instance: Preference model instance
- field: File field
- name: File name/path
"""
@property
def url(self) -> str:
"""Return URL for accessing the file."""
@property
def size(self) -> int:
"""Return file size in bytes."""
def delete(self, save=True):
"""Delete the file and optionally save the model."""Serializers for temporal data with timezone support and proper formatting.
class DurationSerializer(BaseSerializer):
"""
Serialize timedelta objects to/from database strings.
Handles: timedelta(days=1, hours=2) -> "1 day, 2:00:00"
"""
@classmethod
def to_python(cls, value):
"""Convert string to timedelta."""
@classmethod
def to_db(cls, value) -> str:
"""Convert timedelta to string."""
class DateSerializer(BaseSerializer):
"""
Serialize date objects to/from ISO format strings.
Handles: date(2023, 12, 25) -> "2023-12-25"
"""
@classmethod
def to_python(cls, value):
"""Convert ISO string to date."""
@classmethod
def to_db(cls, value) -> str:
"""Convert date to ISO string."""
class DateTimeSerializer(BaseSerializer):
"""
Serialize datetime objects with timezone handling.
Handles: datetime(2023, 12, 25, 10, 30) -> "2023-12-25T10:30:00Z"
Supports timezone conversion and UTC normalization.
"""
@classmethod
def to_python(cls, value):
"""Convert ISO string to datetime with timezone."""
@classmethod
def to_db(cls, value) -> str:
"""Convert datetime to ISO string with timezone."""
class TimeSerializer(BaseSerializer):
"""
Serialize time objects to/from ISO format strings.
Handles: time(14, 30, 45) -> "14:30:45"
"""
@classmethod
def to_python(cls, value):
"""Convert ISO string to time."""
@classmethod
def to_db(cls, value) -> str:
"""Convert time to ISO string."""Special markers and exception classes for serialization error handling.
class UnsetValue:
"""
Marker class for unset preference values.
Used to distinguish between None values and truly unset preferences.
"""
def __bool__(self) -> bool:
"""Always evaluates to False."""
return False
# Global instance for unset values
UNSET: UnsetValue
class SerializationError(Exception):
"""
Exception raised when serialization/deserialization fails.
Provides context about serialization failures with
details about the value and operation that failed.
"""
def __init__(self, message, value=None, operation=None):
"""
Initialize serialization error.
Args:
- message: Error description
- value: Value that caused the error
- operation: Serialization operation (serialize/deserialize)
"""
self.value = value
self.operation = operation
super().__init__(message)from dynamic_preferences.serializers import StringSerializer, BooleanSerializer
# String serialization
value = "Hello, World!"
serialized = StringSerializer.serialize(value)
print(serialized) # "Hello, World!"
deserialized = StringSerializer.deserialize(serialized)
print(deserialized) # "Hello, World!"
# Boolean serialization
bool_value = True
serialized = BooleanSerializer.serialize(bool_value)
print(serialized) # "True"
deserialized = BooleanSerializer.deserialize(serialized)
print(deserialized) # Truefrom django.contrib.auth.models import User
from dynamic_preferences.serializers import ModelSerializer
# Create model serializer
user_serializer = ModelSerializer(model_class=User)
# Serialize user instance
user = User.objects.get(pk=1)
serialized = user_serializer.to_db(user)
print(serialized) # "1"
# Deserialize back to user instance
deserialized = user_serializer.to_python(serialized)
print(deserialized) # <User: username>
# Multiple users
from dynamic_preferences.serializers import ModelMultipleSerializer
multi_serializer = ModelMultipleSerializer(model_class=User)
users = User.objects.filter(is_active=True)[:3]
serialized = multi_serializer.to_db(list(users))
print(serialized) # "[1, 2, 3]"
deserialized = multi_serializer.to_python(serialized)
print(deserialized) # [<User: user1>, <User: user2>, <User: user3>]from datetime import date, datetime, time, timedelta
from dynamic_preferences.serializers import (
DateSerializer, DateTimeSerializer, TimeSerializer, DurationSerializer
)
# Date serialization
today = date.today()
serialized = DateSerializer.serialize(today)
print(serialized) # "2023-12-25"
deserialized = DateSerializer.deserialize(serialized)
print(deserialized) # datetime.date(2023, 12, 25)
# DateTime with timezone
now = datetime.now()
serialized = DateTimeSerializer.serialize(now)
print(serialized) # "2023-12-25T10:30:00+00:00"
# Duration serialization
duration = timedelta(days=1, hours=2, minutes=30)
serialized = DurationSerializer.serialize(duration)
print(serialized) # "1 day, 2:30:00"from dynamic_preferences.serializers import FileSerializer
from django.core.files.uploadedfile import SimpleUploadedFile
# Create file serializer with upload configuration
file_serializer = FileSerializer(
upload_path='preferences/files/',
storage=default_storage
)
# Handle file upload
uploaded_file = SimpleUploadedFile(
"test.txt",
b"file content",
content_type="text/plain"
)
# Serialize file (saves to storage)
file_path = file_serializer.to_db(uploaded_file)
print(file_path) # "preferences/files/test.txt"
# Deserialize back to file object
file_obj = file_serializer.to_python(file_path)
print(file_obj.url) # "/media/preferences/files/test.txt"
print(file_obj.size) # 12import json
from dynamic_preferences.serializers import BaseSerializer, SerializationError
class JSONSerializer(BaseSerializer):
"""Custom serializer for JSON data."""
@classmethod
def to_python(cls, value):
try:
if value == '':
return {}
return json.loads(value)
except (json.JSONDecodeError, TypeError) as e:
raise SerializationError(f"Invalid JSON: {e}", value, 'deserialize')
@classmethod
def to_db(cls, value):
try:
return json.dumps(value, ensure_ascii=False)
except (TypeError, ValueError) as e:
raise SerializationError(f"Cannot serialize to JSON: {e}", value, 'serialize')
# Usage with custom preference type
from dynamic_preferences.types import BasePreferenceType
class JSONPreference(BasePreferenceType):
serializer = JSONSerializer()
field_class = forms.CharField
def setup_field(self, **kwargs):
field = super().setup_field(**kwargs)
field.widget = forms.Textarea(attrs={'rows': 4})
return field
def validate(self, value):
# Ensure it's valid JSON-serializable data
try:
json.dumps(value)
except (TypeError, ValueError):
raise ValidationError("Value must be JSON serializable")
# Register and use
@global_preferences_registry.register
class APIConfiguration(JSONPreference):
section = Section('api')
name = 'config'
default = {'timeout': 30, 'retries': 3}
verbose_name = 'API Configuration'
# Usage
global_preferences = global_preferences_registry.manager()
config = global_preferences['api__config']
print(config) # {'timeout': 30, 'retries': 3}
# Update configuration
global_preferences['api__config'] = {
'timeout': 60,
'retries': 5,
'base_url': 'https://api.example.com'
}from dynamic_preferences.serializers import SerializationError
try:
# This will fail for invalid data
result = IntegerSerializer.deserialize("not_a_number")
except SerializationError as e:
print(f"Serialization failed: {e}")
print(f"Value: {e.value}")
print(f"Operation: {e.operation}")
# Custom error handling in serializers
class SafeIntegerSerializer(IntegerSerializer):
@classmethod
def to_python(cls, value, default=0):
try:
return super().to_python(value)
except SerializationError:
return default
# Usage with fallback
result = SafeIntegerSerializer.to_python("invalid", default=42)
print(result) # 42Install with Tessl CLI
npx tessl i tessl/pypi-django-dynamic-preferences