or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

basic-types.mdcompound-types.mdcontrib-modules.mddynamic-fields.mdexceptions.mdindex.mdmodels.mdnetwork-types.mdutilities.md

dynamic-fields.mddocs/

0

# Dynamic Fields

1

2

Advanced field functionality for computed properties, serializable fields, and dynamic field generation in Schematics. These features enable flexible data modeling with calculated values, custom serialization, and runtime field creation.

3

4

## Capabilities

5

6

### Serializable Fields

7

8

Create computed fields that appear in model serialization with custom calculation logic.

9

10

```python { .api }

11

def serializable(func=None, **field_kwargs):

12

"""

13

Decorator for creating serializable computed fields.

14

15

Creates fields that compute their values dynamically and include

16

the results in model export and serialization operations.

17

18

Args:

19

func (callable, optional): Function to compute field value

20

**field_kwargs: Additional field options (type, required, etc.)

21

22

Returns:

23

Serializable field descriptor

24

"""

25

26

class Serializable:

27

"""

28

Dynamic serializable field descriptor for computed properties.

29

30

Enables creation of fields that compute values at export time

31

based on other model data or external calculations.

32

"""

33

34

def __init__(self, **kwargs):

35

"""

36

Initialize serializable field.

37

38

Args:

39

**kwargs: Field configuration options

40

"""

41

```

42

43

### Calculated Fields

44

45

Create fields with values calculated from other model fields.

46

47

```python { .api }

48

def calculated(func, **field_kwargs):

49

"""

50

Create calculated fields based on other model data.

51

52

Calculated fields automatically compute their values when accessed

53

based on the current state of other model fields.

54

55

Args:

56

func (callable): Function to calculate field value

57

**field_kwargs: Additional field options

58

59

Returns:

60

Calculated field descriptor

61

"""

62

```

63

64

### Dynamic Field Creation

65

66

Runtime field creation and model customization capabilities.

67

68

```python { .api }

69

class DynamicModel(Model):

70

"""

71

Model subclass supporting runtime field addition.

72

73

Enables dynamic model schemas that can be modified at runtime

74

based on configuration or external requirements.

75

"""

76

77

@classmethod

78

def add_field(cls, name, field_type):

79

"""

80

Add field to model class at runtime.

81

82

Args:

83

name (str): Field name

84

field_type (BaseType): Field type instance

85

"""

86

```

87

88

## Usage Examples

89

90

### Basic Serializable Fields

91

92

```python

93

from schematics.models import Model

94

from schematics.types import StringType, IntType, DateTimeType, serializable

95

from datetime import datetime

96

97

class User(Model):

98

first_name = StringType(required=True)

99

last_name = StringType(required=True)

100

birth_year = IntType()

101

created_at = DateTimeType(default=datetime.utcnow)

102

103

@serializable

104

def full_name(self):

105

"""Computed full name from first and last name."""

106

return f"{self.first_name} {self.last_name}"

107

108

@serializable

109

def age(self):

110

"""Calculated age from birth year."""

111

if self.birth_year:

112

return datetime.now().year - self.birth_year

113

return None

114

115

@serializable

116

def account_age_days(self):

117

"""Days since account creation."""

118

if self.created_at:

119

return (datetime.utcnow() - self.created_at).days

120

return 0

121

122

# Usage

123

user = User({

124

'first_name': 'John',

125

'last_name': 'Doe',

126

'birth_year': 1990

127

})

128

129

# Computed fields available in serialization

130

user_data = user.to_primitive()

131

print(user_data)

132

# {

133

# 'first_name': 'John',

134

# 'last_name': 'Doe',

135

# 'birth_year': 1990,

136

# 'created_at': '2024-01-15T10:30:00.123456Z',

137

# 'full_name': 'John Doe',

138

# 'age': 34,

139

# 'account_age_days': 0

140

# }

141

```

142

143

### Advanced Serializable Fields with Type Hints

144

145

```python

146

from schematics.types import FloatType, ListType, StringType

147

148

class Product(Model):

149

name = StringType(required=True)

150

base_price = FloatType(required=True, min_value=0)

151

tax_rate = FloatType(default=0.08, min_value=0, max_value=1)

152

discount_percent = FloatType(default=0, min_value=0, max_value=100)

153

tags = ListType(StringType())

154

155

@serializable(type=FloatType())

156

def tax_amount(self):

157

"""Calculate tax amount."""

158

return self.base_price * self.tax_rate

159

160

@serializable(type=FloatType())

161

def discount_amount(self):

162

"""Calculate discount amount."""

163

return self.base_price * (self.discount_percent / 100)

164

165

@serializable(type=FloatType())

166

def final_price(self):

167

"""Calculate final price after tax and discount."""

168

return self.base_price + self.tax_amount - self.discount_amount

169

170

@serializable(type=StringType())

171

def tag_summary(self):

172

"""Create summary of product tags."""

173

if self.tags:

174

return ', '.join(self.tags)

175

return 'No tags'

176

177

@serializable(type=StringType())

178

def price_tier(self):

179

"""Determine price tier based on final price."""

180

if self.final_price < 20:

181

return 'Budget'

182

elif self.final_price < 100:

183

return 'Standard'

184

else:

185

return 'Premium'

186

187

# Usage

188

product = Product({

189

'name': 'Wireless Headphones',

190

'base_price': 99.99,

191

'tax_rate': 0.08,

192

'discount_percent': 15,

193

'tags': ['electronics', 'audio', 'wireless']

194

})

195

196

print(product.to_primitive())

197

# Includes all computed fields: tax_amount, discount_amount, final_price, etc.

198

```

199

200

### Calculated Fields for Data Aggregation

201

202

```python

203

from schematics.types import ModelType, ListType, calculated

204

205

class OrderItem(Model):

206

product_name = StringType(required=True)

207

quantity = IntType(required=True, min_value=1)

208

unit_price = FloatType(required=True, min_value=0)

209

210

@serializable(type=FloatType())

211

def line_total(self):

212

return self.quantity * self.unit_price

213

214

class Order(Model):

215

order_id = StringType(required=True)

216

customer_name = StringType(required=True)

217

items = ListType(ModelType(OrderItem), required=True, min_size=1)

218

shipping_cost = FloatType(default=0, min_value=0)

219

220

@calculated(type=FloatType())

221

def subtotal(self):

222

"""Calculate subtotal from all line items."""

223

return sum(item.line_total for item in self.items)

224

225

@calculated(type=IntType())

226

def total_items(self):

227

"""Count total quantity across all items."""

228

return sum(item.quantity for item in self.items)

229

230

@calculated(type=FloatType())

231

def total_amount(self):

232

"""Calculate final total including shipping."""

233

return self.subtotal + self.shipping_cost

234

235

@serializable(type=StringType())

236

def order_summary(self):

237

"""Generate human-readable order summary."""

238

return f"Order {self.order_id}: {self.total_items} items, ${self.total_amount:.2f}"

239

240

# Usage

241

order = Order({

242

'order_id': 'ORD-001',

243

'customer_name': 'Jane Smith',

244

'shipping_cost': 5.99,

245

'items': [

246

{'product_name': 'Widget A', 'quantity': 2, 'unit_price': 12.50},

247

{'product_name': 'Widget B', 'quantity': 1, 'unit_price': 25.00}

248

]

249

})

250

251

print(f"Subtotal: ${order.subtotal:.2f}") # $50.00

252

print(f"Total items: {order.total_items}") # 3

253

print(f"Final total: ${order.total_amount:.2f}") # $55.99

254

print(order.order_summary) # "Order ORD-001: 3 items, $55.99"

255

```

256

257

### Dynamic Model Creation

258

259

```python

260

def create_survey_model(questions):

261

"""

262

Dynamically create a survey model based on question configuration.

263

264

Args:

265

questions (list): List of question dictionaries

266

267

Returns:

268

Model: Dynamically created survey model class

269

"""

270

271

class SurveyResponse(Model):

272

respondent_id = StringType(required=True)

273

submitted_at = DateTimeType(default=datetime.utcnow)

274

275

@serializable(type=IntType())

276

def response_count(self):

277

"""Count non-empty responses."""

278

count = 0

279

for field_name, field in self._fields.items():

280

if field_name not in ['respondent_id', 'submitted_at']:

281

value = getattr(self, field_name, None)

282

if value is not None and value != '':

283

count += 1

284

return count

285

286

# Add dynamic fields based on questions

287

for question in questions:

288

field_name = question['name']

289

field_type = question['type']

290

291

if field_type == 'text':

292

field = StringType(required=question.get('required', False))

293

elif field_type == 'number':

294

field = IntType(required=question.get('required', False))

295

elif field_type == 'choice':

296

field = StringType(

297

required=question.get('required', False),

298

choices=question.get('choices', [])

299

)

300

elif field_type == 'multiple_choice':

301

field = ListType(StringType(choices=question.get('choices', [])))

302

else:

303

field = StringType()

304

305

# Add field to model class

306

setattr(SurveyResponse, field_name, field)

307

308

return SurveyResponse

309

310

# Define survey questions

311

survey_questions = [

312

{'name': 'satisfaction', 'type': 'choice', 'required': True,

313

'choices': ['very_satisfied', 'satisfied', 'neutral', 'dissatisfied']},

314

{'name': 'recommendation_score', 'type': 'number', 'required': True},

315

{'name': 'feedback', 'type': 'text', 'required': False},

316

{'name': 'interests', 'type': 'multiple_choice',

317

'choices': ['tech', 'sports', 'music', 'travel']}

318

]

319

320

# Create dynamic survey model

321

SurveyModel = create_survey_model(survey_questions)

322

323

# Use the dynamically created model

324

response = SurveyModel({

325

'respondent_id': 'RESP-001',

326

'satisfaction': 'satisfied',

327

'recommendation_score': 8,

328

'feedback': 'Great service!',

329

'interests': ['tech', 'music']

330

})

331

332

response.validate()

333

print(f"Response count: {response.response_count}") # 4

334

print(response.to_primitive())

335

```

336

337

### Conditional Serializable Fields

338

339

```python

340

class Report(Model):

341

title = StringType(required=True)

342

data = ListType(IntType(), required=True)

343

include_statistics = BooleanType(default=False)

344

include_chart = BooleanType(default=False)

345

346

@serializable(type=FloatType())

347

def average(self):

348

"""Calculate average of data points."""

349

if self.data:

350

return sum(self.data) / len(self.data)

351

return 0

352

353

@serializable(type=IntType())

354

def data_count(self):

355

"""Count of data points."""

356

return len(self.data) if self.data else 0

357

358

@serializable

359

def statistics(self):

360

"""Include detailed statistics if requested."""

361

if not self.include_statistics:

362

return None

363

364

if not self.data:

365

return None

366

367

data_sorted = sorted(self.data)

368

return {

369

'min': min(data_sorted),

370

'max': max(data_sorted),

371

'median': data_sorted[len(data_sorted) // 2],

372

'range': max(data_sorted) - min(data_sorted)

373

}

374

375

@serializable

376

def chart_config(self):

377

"""Include chart configuration if requested."""

378

if not self.include_chart:

379

return None

380

381

return {

382

'type': 'line',

383

'data': self.data,

384

'title': self.title,

385

'y_axis_label': 'Values'

386

}

387

388

# Usage with different configurations

389

basic_report = Report({

390

'title': 'Sales Data',

391

'data': [100, 150, 120, 180, 200]

392

})

393

394

detailed_report = Report({

395

'title': 'Sales Data',

396

'data': [100, 150, 120, 180, 200],

397

'include_statistics': True,

398

'include_chart': True

399

})

400

401

basic_data = basic_report.to_primitive()

402

# Contains: title, data, average, data_count (statistics and chart_config are None)

403

404

detailed_data = detailed_report.to_primitive()

405

# Contains: all basic fields plus statistics and chart_config objects

406

```