or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

admin-integration.mdcore-models.mddjango-integration.mdforms-views.mdindex.mdpreference-types.mdregistries.mdrest-api.mdserialization.mdsignals.mduser-preferences.md

serialization.mddocs/

0

# Serialization

1

2

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.

3

4

## Capabilities

5

6

### Base Serializer Classes

7

8

Foundation classes for implementing serialization logic with support for both class-based and instance-based serializers.

9

10

```python { .api }

11

class BaseSerializer:

12

"""

13

Base serializer for converting Python variables to database strings.

14

15

Provides the foundation for all serialization operations with

16

class-based methods for stateless serialization.

17

"""

18

19

@classmethod

20

def serialize(cls, value, **kwargs) -> str:

21

"""

22

Convert Python value to database string representation.

23

24

Args:

25

- value: Python value to serialize

26

- **kwargs: Additional serialization options

27

28

Returns:

29

String representation suitable for database storage

30

31

Raises:

32

- SerializationError: If serialization fails

33

"""

34

35

@classmethod

36

def deserialize(cls, value, **kwargs):

37

"""

38

Convert database string back to Python value.

39

40

Args:

41

- value: String value from database

42

- **kwargs: Additional deserialization options

43

44

Returns:

45

Python value

46

47

Raises:

48

- SerializationError: If deserialization fails

49

"""

50

51

@classmethod

52

def to_python(cls, value, **kwargs):

53

"""

54

Abstract method for converting string to Python value.

55

56

Must be implemented by subclasses.

57

58

Args:

59

- value: String value to convert

60

- **kwargs: Additional conversion options

61

62

Returns:

63

Python value

64

"""

65

66

@classmethod

67

def to_db(cls, value, **kwargs) -> str:

68

"""

69

Convert Python value to database string.

70

71

Args:

72

- value: Python value to convert

73

- **kwargs: Additional conversion options

74

75

Returns:

76

Database-safe string representation

77

"""

78

79

@classmethod

80

def clean_to_db_value(cls, value):

81

"""

82

Clean and validate value before database storage.

83

84

Args:

85

- value: Value to clean

86

87

Returns:

88

Cleaned value ready for serialization

89

"""

90

91

class InstanciatedSerializer(BaseSerializer):

92

"""

93

Instance-based serializer for cases requiring additional context.

94

95

Used when serialization requires additional data or state

96

that cannot be handled by class methods alone.

97

"""

98

99

def __init__(self, **kwargs):

100

"""

101

Initialize serializer with context data.

102

103

Args:

104

- **kwargs: Context data for serialization

105

"""

106

```

107

108

### Basic Type Serializers

109

110

Serializers for fundamental Python data types with proper type conversion and validation.

111

112

```python { .api }

113

class BooleanSerializer(BaseSerializer):

114

"""

115

Serialize boolean values to/from database strings.

116

117

Handles: True/False -> "True"/"False"

118

"""

119

120

@classmethod

121

def to_python(cls, value) -> bool:

122

"""Convert string to boolean."""

123

124

@classmethod

125

def to_db(cls, value) -> str:

126

"""Convert boolean to string."""

127

128

class IntegerSerializer(BaseSerializer):

129

"""

130

Serialize integer values to/from database strings.

131

132

Handles: 42 -> "42"

133

Alias: IntSerializer

134

"""

135

136

@classmethod

137

def to_python(cls, value) -> int:

138

"""Convert string to integer."""

139

140

@classmethod

141

def to_db(cls, value) -> str:

142

"""Convert integer to string."""

143

144

# Alias for backward compatibility

145

IntSerializer = IntegerSerializer

146

147

class DecimalSerializer(BaseSerializer):

148

"""

149

Serialize Decimal values to/from database strings.

150

151

Handles: Decimal('123.45') -> "123.45"

152

Preserves precision for financial/scientific calculations.

153

"""

154

155

@classmethod

156

def to_python(cls, value):

157

"""Convert string to Decimal."""

158

159

@classmethod

160

def to_db(cls, value) -> str:

161

"""Convert Decimal to string."""

162

163

class FloatSerializer(BaseSerializer):

164

"""

165

Serialize float values to/from database strings.

166

167

Handles: 3.14159 -> "3.14159"

168

"""

169

170

@classmethod

171

def to_python(cls, value) -> float:

172

"""Convert string to float."""

173

174

@classmethod

175

def to_db(cls, value) -> str:

176

"""Convert float to string."""

177

178

class StringSerializer(BaseSerializer):

179

"""

180

Serialize string values with optional HTML escaping.

181

182

Handles: "Hello World" -> "Hello World"

183

Supports HTML escaping for security.

184

"""

185

186

@classmethod

187

def to_python(cls, value) -> str:

188

"""Convert database string to Python string."""

189

190

@classmethod

191

def to_db(cls, value) -> str:

192

"""Convert Python string to database string."""

193

```

194

195

### Collection Serializers

196

197

Serializers for handling multiple values and complex data structures.

198

199

```python { .api }

200

class MultipleSerializer(BaseSerializer):

201

"""

202

Serialize multiple choice values as JSON arrays.

203

204

Handles: ['choice1', 'choice2'] -> '["choice1", "choice2"]'

205

"""

206

207

@classmethod

208

def to_python(cls, value) -> list:

209

"""Convert JSON string to Python list."""

210

211

@classmethod

212

def to_db(cls, value) -> str:

213

"""Convert Python list to JSON string."""

214

```

215

216

### Model-Related Serializers

217

218

Serializers for handling Django model instances and relationships.

219

220

```python { .api }

221

class ModelSerializer(InstanciatedSerializer):

222

"""

223

Serialize Django model instances by primary key.

224

225

Handles model instance -> PK string -> model instance

226

Requires model class context for deserialization.

227

"""

228

229

def __init__(self, model_class=None, **kwargs):

230

"""

231

Initialize with model class for lookups.

232

233

Args:

234

- model_class: Django model class

235

- **kwargs: Additional context

236

"""

237

self.model_class = model_class

238

super().__init__(**kwargs)

239

240

def to_python(self, value):

241

"""

242

Convert primary key to model instance.

243

244

Args:

245

- value: Primary key value

246

247

Returns:

248

Model instance or None

249

"""

250

251

def to_db(self, value) -> str:

252

"""

253

Convert model instance to primary key string.

254

255

Args:

256

- value: Model instance

257

258

Returns:

259

Primary key as string

260

"""

261

262

class ModelMultipleSerializer(ModelSerializer):

263

"""

264

Serialize multiple Django model instances.

265

266

Handles: [model1, model2] -> "[pk1, pk2]" -> [model1, model2]

267

"""

268

269

def to_python(self, value) -> list:

270

"""Convert PK list to model instance list."""

271

272

def to_db(self, value) -> str:

273

"""Convert model instance list to PK JSON string."""

274

```

275

276

### File Serializers

277

278

Serializers for handling file uploads and file references.

279

280

```python { .api }

281

class FileSerializer(InstanciatedSerializer):

282

"""

283

Serialize file uploads with proper file handling.

284

285

Handles file storage, path management, and cleanup.

286

"""

287

288

def __init__(self, upload_path=None, storage=None, **kwargs):

289

"""

290

Initialize with file handling configuration.

291

292

Args:

293

- upload_path: Path for file uploads

294

- storage: Django storage backend

295

- **kwargs: Additional context

296

"""

297

self.upload_path = upload_path

298

self.storage = storage

299

super().__init__(**kwargs)

300

301

def to_python(self, value):

302

"""

303

Convert file path to file object.

304

305

Args:

306

- value: File path string

307

308

Returns:

309

File object or None

310

"""

311

312

def to_db(self, value) -> str:

313

"""

314

Store file and return path string.

315

316

Args:

317

- value: File object

318

319

Returns:

320

File path as string

321

"""

322

323

class PreferenceFieldFile:

324

"""

325

FieldFile subclass for preference files.

326

327

Provides file-like interface for preference file handling

328

with proper integration with Django's file storage system.

329

"""

330

331

def __init__(self, instance, field, name):

332

"""

333

Initialize preference field file.

334

335

Args:

336

- instance: Preference model instance

337

- field: File field

338

- name: File name/path

339

"""

340

341

@property

342

def url(self) -> str:

343

"""Return URL for accessing the file."""

344

345

@property

346

def size(self) -> int:

347

"""Return file size in bytes."""

348

349

def delete(self, save=True):

350

"""Delete the file and optionally save the model."""

351

```

352

353

### Date/Time Serializers

354

355

Serializers for temporal data with timezone support and proper formatting.

356

357

```python { .api }

358

class DurationSerializer(BaseSerializer):

359

"""

360

Serialize timedelta objects to/from database strings.

361

362

Handles: timedelta(days=1, hours=2) -> "1 day, 2:00:00"

363

"""

364

365

@classmethod

366

def to_python(cls, value):

367

"""Convert string to timedelta."""

368

369

@classmethod

370

def to_db(cls, value) -> str:

371

"""Convert timedelta to string."""

372

373

class DateSerializer(BaseSerializer):

374

"""

375

Serialize date objects to/from ISO format strings.

376

377

Handles: date(2023, 12, 25) -> "2023-12-25"

378

"""

379

380

@classmethod

381

def to_python(cls, value):

382

"""Convert ISO string to date."""

383

384

@classmethod

385

def to_db(cls, value) -> str:

386

"""Convert date to ISO string."""

387

388

class DateTimeSerializer(BaseSerializer):

389

"""

390

Serialize datetime objects with timezone handling.

391

392

Handles: datetime(2023, 12, 25, 10, 30) -> "2023-12-25T10:30:00Z"

393

Supports timezone conversion and UTC normalization.

394

"""

395

396

@classmethod

397

def to_python(cls, value):

398

"""Convert ISO string to datetime with timezone."""

399

400

@classmethod

401

def to_db(cls, value) -> str:

402

"""Convert datetime to ISO string with timezone."""

403

404

class TimeSerializer(BaseSerializer):

405

"""

406

Serialize time objects to/from ISO format strings.

407

408

Handles: time(14, 30, 45) -> "14:30:45"

409

"""

410

411

@classmethod

412

def to_python(cls, value):

413

"""Convert ISO string to time."""

414

415

@classmethod

416

def to_db(cls, value) -> str:

417

"""Convert time to ISO string."""

418

```

419

420

### Special Values and Exceptions

421

422

Special markers and exception classes for serialization error handling.

423

424

```python { .api }

425

class UnsetValue:

426

"""

427

Marker class for unset preference values.

428

429

Used to distinguish between None values and truly unset preferences.

430

"""

431

432

def __bool__(self) -> bool:

433

"""Always evaluates to False."""

434

return False

435

436

# Global instance for unset values

437

UNSET: UnsetValue

438

439

class SerializationError(Exception):

440

"""

441

Exception raised when serialization/deserialization fails.

442

443

Provides context about serialization failures with

444

details about the value and operation that failed.

445

"""

446

447

def __init__(self, message, value=None, operation=None):

448

"""

449

Initialize serialization error.

450

451

Args:

452

- message: Error description

453

- value: Value that caused the error

454

- operation: Serialization operation (serialize/deserialize)

455

"""

456

self.value = value

457

self.operation = operation

458

super().__init__(message)

459

```

460

461

## Usage Examples

462

463

### Basic Serialization

464

465

```python

466

from dynamic_preferences.serializers import StringSerializer, BooleanSerializer

467

468

# String serialization

469

value = "Hello, World!"

470

serialized = StringSerializer.serialize(value)

471

print(serialized) # "Hello, World!"

472

deserialized = StringSerializer.deserialize(serialized)

473

print(deserialized) # "Hello, World!"

474

475

# Boolean serialization

476

bool_value = True

477

serialized = BooleanSerializer.serialize(bool_value)

478

print(serialized) # "True"

479

deserialized = BooleanSerializer.deserialize(serialized)

480

print(deserialized) # True

481

```

482

483

### Model Serialization

484

485

```python

486

from django.contrib.auth.models import User

487

from dynamic_preferences.serializers import ModelSerializer

488

489

# Create model serializer

490

user_serializer = ModelSerializer(model_class=User)

491

492

# Serialize user instance

493

user = User.objects.get(pk=1)

494

serialized = user_serializer.to_db(user)

495

print(serialized) # "1"

496

497

# Deserialize back to user instance

498

deserialized = user_serializer.to_python(serialized)

499

print(deserialized) # <User: username>

500

501

# Multiple users

502

from dynamic_preferences.serializers import ModelMultipleSerializer

503

504

multi_serializer = ModelMultipleSerializer(model_class=User)

505

users = User.objects.filter(is_active=True)[:3]

506

serialized = multi_serializer.to_db(list(users))

507

print(serialized) # "[1, 2, 3]"

508

deserialized = multi_serializer.to_python(serialized)

509

print(deserialized) # [<User: user1>, <User: user2>, <User: user3>]

510

```

511

512

### Date/Time Serialization

513

514

```python

515

from datetime import date, datetime, time, timedelta

516

from dynamic_preferences.serializers import (

517

DateSerializer, DateTimeSerializer, TimeSerializer, DurationSerializer

518

)

519

520

# Date serialization

521

today = date.today()

522

serialized = DateSerializer.serialize(today)

523

print(serialized) # "2023-12-25"

524

deserialized = DateSerializer.deserialize(serialized)

525

print(deserialized) # datetime.date(2023, 12, 25)

526

527

# DateTime with timezone

528

now = datetime.now()

529

serialized = DateTimeSerializer.serialize(now)

530

print(serialized) # "2023-12-25T10:30:00+00:00"

531

532

# Duration serialization

533

duration = timedelta(days=1, hours=2, minutes=30)

534

serialized = DurationSerializer.serialize(duration)

535

print(serialized) # "1 day, 2:30:00"

536

```

537

538

### File Serialization

539

540

```python

541

from dynamic_preferences.serializers import FileSerializer

542

from django.core.files.uploadedfile import SimpleUploadedFile

543

544

# Create file serializer with upload configuration

545

file_serializer = FileSerializer(

546

upload_path='preferences/files/',

547

storage=default_storage

548

)

549

550

# Handle file upload

551

uploaded_file = SimpleUploadedFile(

552

"test.txt",

553

b"file content",

554

content_type="text/plain"

555

)

556

557

# Serialize file (saves to storage)

558

file_path = file_serializer.to_db(uploaded_file)

559

print(file_path) # "preferences/files/test.txt"

560

561

# Deserialize back to file object

562

file_obj = file_serializer.to_python(file_path)

563

print(file_obj.url) # "/media/preferences/files/test.txt"

564

print(file_obj.size) # 12

565

```

566

567

### Custom Serializer

568

569

```python

570

import json

571

from dynamic_preferences.serializers import BaseSerializer, SerializationError

572

573

class JSONSerializer(BaseSerializer):

574

"""Custom serializer for JSON data."""

575

576

@classmethod

577

def to_python(cls, value):

578

try:

579

if value == '':

580

return {}

581

return json.loads(value)

582

except (json.JSONDecodeError, TypeError) as e:

583

raise SerializationError(f"Invalid JSON: {e}", value, 'deserialize')

584

585

@classmethod

586

def to_db(cls, value):

587

try:

588

return json.dumps(value, ensure_ascii=False)

589

except (TypeError, ValueError) as e:

590

raise SerializationError(f"Cannot serialize to JSON: {e}", value, 'serialize')

591

592

# Usage with custom preference type

593

from dynamic_preferences.types import BasePreferenceType

594

595

class JSONPreference(BasePreferenceType):

596

serializer = JSONSerializer()

597

field_class = forms.CharField

598

599

def setup_field(self, **kwargs):

600

field = super().setup_field(**kwargs)

601

field.widget = forms.Textarea(attrs={'rows': 4})

602

return field

603

604

def validate(self, value):

605

# Ensure it's valid JSON-serializable data

606

try:

607

json.dumps(value)

608

except (TypeError, ValueError):

609

raise ValidationError("Value must be JSON serializable")

610

611

# Register and use

612

@global_preferences_registry.register

613

class APIConfiguration(JSONPreference):

614

section = Section('api')

615

name = 'config'

616

default = {'timeout': 30, 'retries': 3}

617

verbose_name = 'API Configuration'

618

619

# Usage

620

global_preferences = global_preferences_registry.manager()

621

config = global_preferences['api__config']

622

print(config) # {'timeout': 30, 'retries': 3}

623

624

# Update configuration

625

global_preferences['api__config'] = {

626

'timeout': 60,

627

'retries': 5,

628

'base_url': 'https://api.example.com'

629

}

630

```

631

632

### Error Handling

633

634

```python

635

from dynamic_preferences.serializers import SerializationError

636

637

try:

638

# This will fail for invalid data

639

result = IntegerSerializer.deserialize("not_a_number")

640

except SerializationError as e:

641

print(f"Serialization failed: {e}")

642

print(f"Value: {e.value}")

643

print(f"Operation: {e.operation}")

644

645

# Custom error handling in serializers

646

class SafeIntegerSerializer(IntegerSerializer):

647

@classmethod

648

def to_python(cls, value, default=0):

649

try:

650

return super().to_python(value)

651

except SerializationError:

652

return default

653

654

# Usage with fallback

655

result = SafeIntegerSerializer.to_python("invalid", default=42)

656

print(result) # 42

657

```