or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication-permissions.mdexceptions-status.mdfields-relations.mdindex.mdpagination-filtering.mdrequests-responses.mdrouters-urls.mdserializers.mdviews-viewsets.md

serializers.mddocs/

0

# Serializers

1

2

Django REST Framework serializers provide a powerful system for converting complex data types to native Python datatypes and vice versa. The type stubs provide comprehensive type safety for all serializer operations, field definitions, and validation patterns.

3

4

## Core Serializer Classes

5

6

### BaseSerializer

7

8

```python { .api }

9

class BaseSerializer(Field[Any, Any, Any, _IN]):

10

"""Base class for all serializers with generic instance type support."""

11

12

partial: bool

13

many: bool

14

instance: _IN | None

15

initial_data: Any

16

17

def __init__(

18

self,

19

instance: _IN | None = None,

20

data: Any = empty,

21

**kwargs: Any

22

) -> None: ...

23

24

def is_valid(self, *, raise_exception: bool = False) -> bool: ...

25

def save(self, **kwargs: Any) -> _IN: ...

26

def create(self, validated_data: Any) -> _IN: ...

27

def update(self, instance: _IN, validated_data: Any) -> _IN: ...

28

def to_representation(self, instance: _IN) -> Any: ...

29

```

30

31

**Parameters:**

32

- `instance: _IN | None` - Object instance to serialize or update

33

- `data: Any` - Input data for deserialization

34

- `partial: bool` - Allow partial updates when True

35

- `many: bool` - Serialize multiple objects when True

36

- `**kwargs: Any` - Additional serializer options

37

38

### Serializer

39

40

```python { .api }

41

class Serializer(BaseSerializer[_IN]):

42

"""Main serializer class for custom serializations."""

43

44

def __init__(

45

self,

46

instance: _IN | None = None,

47

data: Any = empty,

48

many: bool = False,

49

partial: bool = False,

50

context: dict[str, Any] | None = None,

51

allow_empty: bool = False,

52

**kwargs: Any

53

) -> None: ...

54

55

def get_fields(self) -> dict[str, Field]: ...

56

def validate(self, attrs: Any) -> Any: ...

57

def to_representation(self, instance: _IN) -> dict[str, Any]: ...

58

```

59

60

**Parameters:**

61

- `context: dict[str, Any] | None` - Additional context for serialization

62

- `allow_empty: bool` - Allow empty values when True

63

64

### ModelSerializer

65

66

```python { .api }

67

class ModelSerializer(Serializer[_MT]):

68

"""Serializer that automatically generates fields from Django models."""

69

70

serializer_field_mapping: dict[type[models.Field], type[Field]]

71

72

class Meta:

73

model: type[_MT]

74

fields: Sequence[str] | Literal["__all__"]

75

exclude: Sequence[str] | None

76

read_only_fields: Sequence[str] | None

77

extra_kwargs: dict[str, dict[str, Any]]

78

depth: int | None

79

80

def get_field_names(self, declared_fields: dict[str, Field], info: Any) -> list[str]: ...

81

def build_field(self, field_name: str, info: Any, model_class: type[Model], nested_depth: int) -> tuple[type[Field], dict[str, Any]]: ...

82

def build_standard_field(self, field_name: str, model_field: models.Field) -> tuple[type[Field], dict[str, Any]]: ...

83

def build_relational_field(self, field_name: str, relation_info: Any) -> tuple[type[Field], dict[str, Any]]: ...

84

def build_nested_field(self, field_name: str, relation_info: Any, nested_depth: int) -> tuple[type[Field], dict[str, Any]]: ...

85

def create(self, validated_data: dict[str, Any]) -> _MT: ...

86

def update(self, instance: _MT, validated_data: dict[str, Any]) -> _MT: ...

87

```

88

89

**Meta Class Parameters:**

90

- `model: type[_MT]` - Django model class to serialize

91

- `fields: Sequence[str] | Literal["__all__"]` - Fields to include ('__all__' or field list)

92

- `exclude: Sequence[str] | None` - Fields to exclude from serialization

93

- `read_only_fields: Sequence[str] | None` - Fields that cannot be modified

94

- `extra_kwargs: dict[str, dict[str, Any]]` - Additional field arguments

95

- `depth: int | None` - Depth of nested serialization (default: None)

96

97

### HyperlinkedModelSerializer

98

99

```python { .api }

100

class HyperlinkedModelSerializer(ModelSerializer[_MT]):

101

"""Model serializer that uses hyperlinked relationships."""

102

103

serializer_related_field: type[HyperlinkedRelatedField]

104

serializer_url_field: type[HyperlinkedIdentityField]

105

url_field_name: str

106

107

class Meta(ModelSerializer.Meta):

108

pass

109

```

110

111

### ListSerializer

112

113

```python { .api }

114

class ListSerializer(BaseSerializer[_IN]):

115

"""Serializer for handling multiple object instances."""

116

117

child: Field | BaseSerializer | None

118

many: bool

119

allow_empty: bool | None

120

max_length: int | None

121

min_length: int | None

122

123

def __init__(

124

self,

125

*args: Any,

126

child: BaseSerializer | None = None,

127

allow_empty: bool = True,

128

max_length: int | None = None,

129

min_length: int | None = None,

130

**kwargs: Any

131

) -> None: ...

132

133

def create(self, validated_data: list[dict[str, Any]]) -> list[_IN]: ...

134

def update(self, instance: list[_IN], validated_data: list[dict[str, Any]]) -> list[_IN]: ...

135

```

136

137

**Parameters:**

138

- `child: BaseSerializer | None` - Serializer for individual items

139

- `allow_empty: bool` - Allow empty lists when True

140

- `max_length: int | None` - Maximum number of items

141

- `min_length: int | None` - Minimum number of items

142

143

## Serializer Configuration

144

145

### Field Declaration

146

147

```python { .api }

148

from rest_framework import serializers

149

150

class BookSerializer(serializers.ModelSerializer[Book]):

151

# Explicit field declarations

152

isbn = serializers.CharField(max_length=13, required=True)

153

rating = serializers.FloatField(min_value=0.0, max_value=5.0)

154

tags = serializers.ListField(child=serializers.CharField())

155

156

# Method fields

157

author_name = serializers.SerializerMethodField()

158

159

class Meta:

160

model = Book

161

fields = ['id', 'title', 'isbn', 'rating', 'tags', 'author_name']

162

read_only_fields = ['id']

163

164

def get_author_name(self, obj: Book) -> str:

165

return f"{obj.author.first_name} {obj.author.last_name}"

166

```

167

168

### Nested Serialization

169

170

```python { .api }

171

class AuthorSerializer(serializers.ModelSerializer[Author]):

172

class Meta:

173

model = Author

174

fields = ['id', 'first_name', 'last_name', 'bio']

175

176

class BookSerializer(serializers.ModelSerializer[Book]):

177

author = AuthorSerializer(read_only=True)

178

author_id = serializers.IntegerField(write_only=True)

179

180

class Meta:

181

model = Book

182

fields = ['id', 'title', 'author', 'author_id', 'published_date']

183

184

def create(self, validated_data: dict[str, Any]) -> Book:

185

author_id = validated_data.pop('author_id')

186

author = Author.objects.get(id=author_id)

187

return Book.objects.create(author=author, **validated_data)

188

```

189

190

### Custom Serializer

191

192

```python { .api }

193

class BookSearchSerializer(serializers.Serializer):

194

"""Custom serializer for search functionality."""

195

196

query = serializers.CharField(max_length=100)

197

author = serializers.CharField(max_length=50, required=False)

198

genre = serializers.ChoiceField(choices=['fiction', 'non-fiction', 'mystery'], required=False)

199

published_after = serializers.DateField(required=False)

200

min_rating = serializers.FloatField(min_value=0.0, max_value=5.0, required=False)

201

202

def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:

203

"""Cross-field validation."""

204

if attrs.get('min_rating', 0) > 4.5 and not attrs.get('author'):

205

raise serializers.ValidationError(

206

"High rating searches require an author filter"

207

)

208

return attrs

209

210

def validate_query(self, value: str) -> str:

211

"""Field-level validation."""

212

if len(value.strip()) < 2:

213

raise serializers.ValidationError("Query must be at least 2 characters")

214

return value.strip()

215

```

216

217

## Validation Capabilities

218

219

### Field-Level Validation

220

221

```python { .api }

222

class UserSerializer(serializers.ModelSerializer[User]):

223

class Meta:

224

model = User

225

fields = ['username', 'email', 'password']

226

extra_kwargs = {

227

'password': {'write_only': True, 'min_length': 8}

228

}

229

230

def validate_username(self, value: str) -> str:

231

"""Validate username field."""

232

if User.objects.filter(username__iexact=value).exists():

233

raise serializers.ValidationError("Username already exists")

234

if not value.replace('_', '').replace('-', '').isalnum():

235

raise serializers.ValidationError("Username can only contain letters, numbers, - and _")

236

return value

237

238

def validate_email(self, value: str) -> str:

239

"""Validate email field."""

240

if User.objects.filter(email__iexact=value).exists():

241

raise serializers.ValidationError("Email already registered")

242

return value.lower()

243

```

244

245

### Object-Level Validation

246

247

```python { .api }

248

class EventSerializer(serializers.ModelSerializer[Event]):

249

class Meta:

250

model = Event

251

fields = ['name', 'start_date', 'end_date', 'max_attendees', 'current_attendees']

252

read_only_fields = ['current_attendees']

253

254

def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:

255

"""Cross-field validation."""

256

start_date = attrs.get('start_date')

257

end_date = attrs.get('end_date')

258

259

if start_date and end_date:

260

if start_date >= end_date:

261

raise serializers.ValidationError(

262

"End date must be after start date"

263

)

264

265

if start_date < timezone.now().date():

266

raise serializers.ValidationError(

267

"Start date cannot be in the past"

268

)

269

270

max_attendees = attrs.get('max_attendees')

271

if max_attendees and max_attendees < 1:

272

raise serializers.ValidationError(

273

"Maximum attendees must be at least 1"

274

)

275

276

return attrs

277

```

278

279

### Custom Validators

280

281

```python { .api }

282

from rest_framework import validators

283

284

def validate_positive(value: int) -> int:

285

"""Ensure value is positive."""

286

if value <= 0:

287

raise serializers.ValidationError("Value must be positive")

288

return value

289

290

class ProductSerializer(serializers.ModelSerializer[Product]):

291

price = serializers.DecimalField(

292

max_digits=10,

293

decimal_places=2,

294

validators=[validate_positive]

295

)

296

297

class Meta:

298

model = Product

299

fields = ['name', 'price', 'category', 'stock_quantity']

300

validators = [

301

validators.UniqueTogetherValidator(

302

queryset=Product.objects.all(),

303

fields=['name', 'category']

304

)

305

]

306

```

307

308

## Serialization Operations

309

310

### Basic Serialization

311

312

```python { .api }

313

# Serializing single object

314

book = Book.objects.get(id=1)

315

serializer = BookSerializer(book)

316

data = serializer.data # dict[str, Any]

317

318

# Serializing multiple objects

319

books = Book.objects.all()

320

serializer = BookSerializer(books, many=True)

321

data = serializer.data # list[dict[str, Any]]

322

323

# Including context

324

serializer = BookSerializer(book, context={'request': request})

325

data = serializer.data

326

```

327

328

### Deserialization and Validation

329

330

```python { .api }

331

# Creating new object

332

data = {'title': 'New Book', 'author_id': 1}

333

serializer = BookSerializer(data=data)

334

335

if serializer.is_valid():

336

book = serializer.save() # Returns Book instance

337

return Response(serializer.data, status=status.HTTP_201_CREATED)

338

else:

339

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

340

341

# Updating existing object

342

book = Book.objects.get(id=1)

343

data = {'title': 'Updated Title'}

344

serializer = BookSerializer(book, data=data, partial=True)

345

346

if serializer.is_valid():

347

updated_book = serializer.save()

348

return Response(serializer.data)

349

else:

350

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

351

```

352

353

### Advanced Serialization Patterns

354

355

```python { .api }

356

class BookListSerializer(serializers.ModelSerializer[Book]):

357

"""Minimal serializer for list views."""

358

author_name = serializers.CharField(source='author.full_name', read_only=True)

359

360

class Meta:

361

model = Book

362

fields = ['id', 'title', 'author_name', 'rating']

363

364

class BookDetailSerializer(serializers.ModelSerializer[Book]):

365

"""Full serializer for detail views."""

366

author = AuthorSerializer(read_only=True)

367

reviews = ReviewSerializer(many=True, read_only=True)

368

369

class Meta:

370

model = Book

371

fields = ['id', 'title', 'description', 'author', 'reviews', 'published_date']

372

373

# Dynamic serializer selection

374

def get_serializer_class(self) -> type[BaseSerializer]:

375

if self.action == 'list':

376

return BookListSerializer

377

return BookDetailSerializer

378

```

379

380

## Utility Functions

381

382

### Serializer Helpers

383

384

```python { .api }

385

def as_serializer_error(exc: Exception) -> dict[str, list[ErrorDetail]]:

386

"""Convert exception to serializer error format."""

387

...

388

389

def raise_errors_on_nested_writes(

390

method_name: str,

391

serializer: BaseSerializer,

392

validated_data: Any

393

) -> None:

394

"""Raise error if nested writes are attempted."""

395

...

396

```

397

398

### Constants

399

400

```python { .api }

401

LIST_SERIALIZER_KWARGS: Sequence[str]

402

LIST_SERIALIZER_KWARGS_REMOVE: Sequence[str]

403

ALL_FIELDS: str # Value: '__all__'

404

```

405

406

## Error Handling

407

408

### Validation Errors

409

410

```python { .api }

411

from rest_framework.exceptions import ValidationError

412

413

# Field-level validation error

414

def validate_age(self, value: int) -> int:

415

if value < 0 or value > 150:

416

raise ValidationError("Age must be between 0 and 150")

417

return value

418

419

# Object-level validation error

420

def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:

421

if attrs['password'] != attrs['confirm_password']:

422

raise ValidationError("Passwords don't match")

423

return attrs

424

```

425

426

### Error Response Format

427

428

```python { .api }

429

# Single field error

430

{

431

"username": ["This field is required."]

432

}

433

434

# Multiple field errors

435

{

436

"username": ["This field is required."],

437

"email": ["Enter a valid email address."]

438

}

439

440

# Non-field errors

441

{

442

"non_field_errors": ["Passwords don't match."]

443

}

444

```

445

446

## Performance Considerations

447

448

### Optimizing Queries

449

450

```python { .api }

451

class AuthorSerializer(serializers.ModelSerializer[Author]):

452

book_count = serializers.SerializerMethodField()

453

454

class Meta:

455

model = Author

456

fields = ['id', 'name', 'book_count']

457

458

def get_book_count(self, obj: Author) -> int:

459

# Use prefetch_related to avoid N+1 queries

460

return obj.books.count()

461

462

# In view:

463

queryset = Author.objects.prefetch_related('books')

464

```

465

466

### Field Selection

467

468

```python { .api }

469

class DynamicFieldsModelSerializer(serializers.ModelSerializer):

470

"""Serializer that allows dynamic field selection."""

471

472

def __init__(self, *args: Any, **kwargs: Any) -> None:

473

fields = kwargs.pop('fields', None)

474

super().__init__(*args, **kwargs)

475

476

if fields is not None:

477

allowed = set(fields)

478

existing = set(self.fields)

479

for field_name in existing - allowed:

480

self.fields.pop(field_name)

481

```

482

483

This comprehensive serializer system provides type-safe data conversion, validation, and serialization for Django REST Framework applications, with full mypy support for catching type errors during development.