or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-api.mderror-handling.mdindex.mdmarshalling-fields.mdmodels-validation.mdrequest-parsing.md

models-validation.mddocs/

0

# Models and Data Validation

1

2

Flask-RESTX provides a comprehensive schema definition system for complex data structures with JSON Schema validation, inheritance support, and automatic documentation generation. Models enable declarative API contracts with comprehensive validation and serve as the foundation for request/response documentation.

3

4

## Capabilities

5

6

### Model Base Classes

7

8

Foundation classes for all model types with common functionality.

9

10

```python { .api }

11

class ModelBase:

12

def __init__(self, name, *args, **kwargs):

13

"""

14

Base class for all models.

15

16

Parameters:

17

- name: Model name for documentation and references

18

- args, kwargs: Additional parameters for subclasses

19

"""

20

21

def inherit(self, name, *parents):

22

"""

23

Create inherited model using Swagger composition pattern.

24

25

Parameters:

26

- name: New model name

27

- parents: Parent models to inherit from

28

29

Returns:

30

New model instance inheriting from parents

31

"""

32

33

def validate(self, data, resolver=None, format_checker=None):

34

"""

35

Validate data against model schema.

36

37

Parameters:

38

- data: Data to validate

39

- resolver: JSON schema resolver for references

40

- format_checker: JSON schema format checker

41

42

Raises:

43

ValidationError: If validation fails

44

"""

45

46

def get_parent(self, name):

47

"""

48

Get parent model by name.

49

50

Parameters:

51

- name: Parent model name

52

53

Returns:

54

Parent model instance

55

56

Raises:

57

ValueError: If parent not found

58

"""

59

60

@property

61

def ancestors(self):

62

"""Set of all ancestor model names."""

63

64

@property

65

def __schema__(self):

66

"""JSON Schema representation of the model."""

67

68

@property

69

def name(self):

70

"""Model name."""

71

```

72

73

### Model Classes

74

75

Concrete model implementations for different use cases.

76

77

```python { .api }

78

class Model(dict):

79

def __init__(self, name, fields_dict=None, mask=None, strict=False, **kwargs):

80

"""

81

Dictionary-based model with field definitions.

82

83

Parameters:

84

- name: Model name

85

- fields_dict: Dictionary of field name to field definition

86

- mask: Optional field mask for selective serialization

87

- strict: Whether to enforce strict validation

88

- kwargs: Additional model parameters

89

"""

90

91

def __getitem__(self, key):

92

"""Get field definition by name."""

93

94

def __setitem__(self, key, value):

95

"""Set field definition by name."""

96

97

@classmethod

98

def clone(cls, name, *parents):

99

"""

100

Create a clone with additional fields.

101

102

Parameters:

103

- name: New model name

104

- parents: Parent models or field dictionaries

105

106

Returns:

107

New model instance

108

"""

109

110

@classmethod

111

def inherit(cls, name, *parents):

112

"""

113

Create inherited model using composition pattern.

114

115

Parameters:

116

- name: New model name

117

- parents: Parent models to inherit from

118

119

Returns:

120

New model instance

121

"""

122

123

def extend(self, name, fields_dict):

124

"""

125

Extend model with additional fields (deprecated).

126

127

Parameters:

128

- name: New model name

129

- fields_dict: Dictionary of additional fields

130

131

Returns:

132

Extended model instance

133

"""

134

135

def validate(self, data, resolver=None, format_checker=None):

136

"""

137

Validate data against model schema.

138

139

Parameters:

140

- data: Data to validate

141

- resolver: JSON schema resolver

142

- format_checker: JSON schema format checker

143

144

Raises:

145

ValidationError: If validation fails

146

"""

147

148

@property

149

def __schema__(self):

150

"""JSON Schema representation."""

151

152

@property

153

def resolved(self):

154

"""Resolved field definitions."""

155

156

class OrderedModel(Model):

157

def __init__(self, name, fields_dict=None, mask=None, strict=False, **kwargs):

158

"""

159

Ordered dictionary-based model preserving field order.

160

161

Parameters:

162

- name: Model name

163

- fields_dict: Ordered dictionary of field definitions

164

- mask: Optional field mask

165

- strict: Whether to enforce strict validation

166

- kwargs: Additional model parameters

167

"""

168

169

class SchemaModel(ModelBase):

170

def __init__(self, name, schema=None, **kwargs):

171

"""

172

JSON Schema-based model.

173

174

Parameters:

175

- name: Model name

176

- schema: JSON Schema definition

177

- kwargs: Additional model parameters

178

"""

179

180

@property

181

def _schema(self):

182

"""Internal schema representation."""

183

184

@property

185

def __schema__(self):

186

"""JSON Schema representation."""

187

```

188

189

### Mask System

190

191

Field filtering and partial response functionality.

192

193

```python { .api }

194

class Mask:

195

def __init__(self, mask=None, skip=False, **kwargs):

196

"""

197

Field mask for selective serialization.

198

199

Parameters:

200

- mask: Mask definition (string, dict, or Mask instance)

201

- skip: Whether to skip missing fields

202

- kwargs: Additional mask parameters

203

"""

204

205

def parse(self, mask):

206

"""

207

Parse mask string into field selection structure.

208

209

Parameters:

210

- mask: Mask string in format '{field1,field2{nested1,nested2}}'

211

"""

212

213

def apply(self, data, skip=False):

214

"""

215

Apply mask to data structure.

216

217

Parameters:

218

- data: Data to filter

219

- skip: Whether to skip missing fields

220

221

Returns:

222

Filtered data structure

223

"""

224

225

def apply(data, mask, skip=False):

226

"""

227

Apply mask to data structure.

228

229

Parameters:

230

- data: Data to filter

231

- mask: Mask instance or definition

232

- skip: Whether to skip missing fields

233

234

Returns:

235

Filtered data according to mask

236

"""

237

238

def format_error(error):

239

"""

240

Format validation error for display.

241

242

Parameters:

243

- error: Validation error instance

244

245

Returns:

246

Formatted error message

247

"""

248

```

249

250

### Mask Exceptions

251

252

Exception types for mask parsing and application errors.

253

254

```python { .api }

255

class MaskError(RestError):

256

def __init__(self, msg):

257

"""

258

Base exception for mask operations.

259

260

Parameters:

261

- msg: Error message

262

"""

263

264

class ParseError(MaskError):

265

def __init__(self, msg):

266

"""

267

Exception raised when mask parsing fails.

268

269

Parameters:

270

- msg: Parse error details

271

"""

272

```

273

274

## Usage Examples

275

276

### Basic Model Definition

277

278

```python

279

from flask_restx import Api, fields

280

281

api = Api()

282

283

# Define a simple model

284

user_model = api.model('User', {

285

'id': fields.Integer(required=True, description='User ID'),

286

'name': fields.String(required=True, description='Full name'),

287

'email': fields.String(required=True, description='Email address'),

288

'active': fields.Boolean(default=True, description='Account status'),

289

'created_at': fields.DateTime(description='Account creation time')

290

})

291

```

292

293

### Model with Nested Objects

294

295

```python

296

# Address model

297

address_model = api.model('Address', {

298

'street': fields.String(required=True),

299

'city': fields.String(required=True),

300

'state': fields.String,

301

'country': fields.String(required=True),

302

'postal_code': fields.String

303

})

304

305

# User model with nested address

306

user_with_address = api.model('UserWithAddress', {

307

'id': fields.Integer(required=True),

308

'name': fields.String(required=True),

309

'email': fields.String(required=True),

310

'address': fields.Nested(address_model),

311

'billing_address': fields.Nested(address_model, allow_null=True)

312

})

313

```

314

315

### Model Inheritance

316

317

```python

318

# Base person model

319

person_model = api.model('Person', {

320

'name': fields.String(required=True),

321

'email': fields.String(required=True),

322

'phone': fields.String

323

})

324

325

# Employee model inheriting from person

326

employee_model = api.inherit('Employee', person_model, {

327

'employee_id': fields.String(required=True),

328

'department': fields.String(required=True),

329

'hire_date': fields.Date,

330

'salary': fields.Float(min=0)

331

})

332

333

# Manager model inheriting from employee

334

manager_model = api.inherit('Manager', employee_model, {

335

'team_size': fields.Integer(min=0),

336

'reports': fields.List(fields.Nested(employee_model))

337

})

338

```

339

340

### Model Extension and Cloning

341

342

```python

343

# Base product model

344

product_model = api.model('Product', {

345

'id': fields.Integer(required=True),

346

'name': fields.String(required=True),

347

'price': fields.Float(required=True, min=0)

348

})

349

350

# Extend with additional fields

351

detailed_product = api.clone('DetailedProduct', product_model, {

352

'description': fields.String,

353

'category': fields.String,

354

'tags': fields.List(fields.String),

355

'in_stock': fields.Boolean(default=True)

356

})

357

358

# Alternative using extend method

359

extended_product = product_model.extend('ExtendedProduct', {

360

'manufacturer': fields.String,

361

'warranty_months': fields.Integer(min=0)

362

})

363

```

364

365

### Schema-based Models

366

367

```python

368

# JSON Schema definition

369

user_schema = {

370

"type": "object",

371

"properties": {

372

"name": {"type": "string", "minLength": 1},

373

"age": {"type": "integer", "minimum": 0, "maximum": 150},

374

"email": {"type": "string", "format": "email"}

375

},

376

"required": ["name", "email"]

377

}

378

379

# Create schema model

380

schema_user_model = api.schema_model('SchemaUser', user_schema)

381

```

382

383

### Model Validation

384

385

```python

386

from flask_restx import Resource, ValidationError

387

388

@api.route('/users')

389

class UserList(Resource):

390

@api.expect(user_model, validate=True)

391

def post(self):

392

# Request payload automatically validated against model

393

data = api.payload

394

395

try:

396

# Manual validation if needed

397

user_model.validate(data)

398

except ValidationError as e:

399

return {'error': str(e)}, 400

400

401

# Process valid data

402

return {'message': 'User created', 'data': data}, 201

403

```

404

405

### Field Masking

406

407

```python

408

from flask_restx import Mask

409

410

# Create mask for selective fields

411

mask = Mask('name,email,address{city,country}')

412

413

# Apply mask to model data

414

user_data = {

415

'id': 1,

416

'name': 'John Doe',

417

'email': 'john@example.com',

418

'phone': '555-1234',

419

'address': {

420

'street': '123 Main St',

421

'city': 'Anytown',

422

'state': 'CA',

423

'country': 'USA',

424

'postal_code': '12345'

425

}

426

}

427

428

# Result includes only masked fields

429

filtered_data = mask.apply(user_data)

430

# {'name': 'John Doe', 'email': 'john@example.com', 'address': {'city': 'Anytown', 'country': 'USA'}}

431

```

432

433

### Model Documentation Integration

434

435

```python

436

@api.route('/users/<int:user_id>')

437

class User(Resource):

438

@api.marshal_with(user_model)

439

@api.doc('get_user')

440

def get(self, user_id):

441

"""Fetch a user by ID"""

442

return find_user(user_id)

443

444

@api.expect(user_model, validate=True)

445

@api.marshal_with(user_model)

446

@api.doc('update_user')

447

@api.response(200, 'User updated', user_model)

448

@api.response(400, 'Validation error')

449

@api.response(404, 'User not found')

450

def put(self, user_id):

451

"""Update a user"""

452

data = api.payload

453

updated_user = update_user(user_id, data)

454

return updated_user

455

```

456

457

### Ordered Models

458

459

```python

460

from collections import OrderedDict

461

462

# Preserve field order in responses

463

ordered_user = api.model('OrderedUser', OrderedDict([

464

('id', fields.Integer(required=True)),

465

('name', fields.String(required=True)),

466

('email', fields.String(required=True)),

467

('created_at', fields.DateTime),

468

('updated_at', fields.DateTime)

469

]))

470

471

# Or use OrderedModel directly

472

ordered_product = OrderedModel('Product', OrderedDict([

473

('id', fields.Integer),

474

('sku', fields.String),

475

('name', fields.String),

476

('price', fields.Float),

477

('availability', fields.Boolean)

478

]))

479

```

480

481

### Advanced Validation Patterns

482

483

```python

484

# Model with complex validation rules

485

order_model = api.model('Order', {

486

'id': fields.Integer(required=True),

487

'customer_id': fields.Integer(required=True, min=1),

488

'items': fields.List(fields.Nested(api.model('OrderItem', {

489

'product_id': fields.Integer(required=True),

490

'quantity': fields.Integer(required=True, min=1),

491

'unit_price': fields.Float(required=True, min=0)

492

})), required=True, min_items=1),

493

'total_amount': fields.Float(required=True, min=0),

494

'order_date': fields.DateTime(required=True),

495

'status': fields.String(required=True, enum=['pending', 'confirmed', 'shipped', 'delivered'])

496

})

497

498

# Custom validation logic

499

@api.route('/orders')

500

class OrderList(Resource):

501

@api.expect(order_model, validate=True)

502

def post(self):

503

data = api.payload

504

505

# Additional business logic validation

506

calculated_total = sum(item['quantity'] * item['unit_price'] for item in data['items'])

507

if abs(calculated_total - data['total_amount']) > 0.01:

508

return {'error': 'Total amount does not match item calculations'}, 400

509

510

return {'message': 'Order created'}, 201

511

```