CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-django-dynamic-preferences

Dynamic global and instance settings for your django project

Pending
Overview
Eval results
Files

serialization.mddocs/

Serialization

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.

Capabilities

Base Serializer Classes

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
        """

Basic Type Serializers

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."""

Collection Serializers

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."""

Model-Related Serializers

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."""

File Serializers

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."""

Date/Time Serializers

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 Values and Exceptions

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)

Usage Examples

Basic Serialization

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)  # True

Model Serialization

from 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>]

Date/Time Serialization

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"

File Serialization

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)  # 12

Custom Serializer

import 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'
}

Error Handling

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)  # 42

Install with Tessl CLI

npx tessl i tessl/pypi-django-dynamic-preferences

docs

admin-integration.md

core-models.md

django-integration.md

forms-views.md

index.md

preference-types.md

registries.md

rest-api.md

serialization.md

signals.md

user-preferences.md

tile.json