or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

admin-integration.mdfile-formats.mdforms-ui.mdindex.mdmanagement-commands.mdresources-fields.mdwidgets-transformation.md

widgets-transformation.mddocs/

0

# Widgets and Data Transformation

1

2

Comprehensive widget system for transforming data between Python objects and serialized formats, including support for various data types and relationships.

3

4

## Capabilities

5

6

### Base Widget Class

7

8

The foundation widget class that all other widgets extend.

9

10

```python { .api }

11

class Widget:

12

def clean(self, value, row=None, **kwargs):

13

"""

14

Transform imported value into appropriate Python object.

15

16

Parameters:

17

- value: Raw value from import data

18

- row: dict, complete row data (optional)

19

- **kwargs: Additional cleaning options

20

21

Returns:

22

Cleaned Python value

23

"""

24

25

def render(self, value, obj=None, **kwargs):

26

"""

27

Transform Python value into export representation.

28

29

Parameters:

30

- value: Python value to export

31

- obj: Model instance being exported (optional)

32

- **kwargs: Additional rendering options

33

34

Returns:

35

Serialized value for export

36

"""

37

```

38

39

### Numeric Widgets

40

41

Widgets for handling numeric data types with validation and formatting.

42

43

```python { .api }

44

class NumberWidget(Widget):

45

"""Base widget for numeric data with locale support."""

46

47

def __init__(self, **kwargs):

48

"""

49

Initialize numeric widget.

50

51

Parameters:

52

- **kwargs: Widget configuration options

53

"""

54

55

class FloatWidget(NumberWidget):

56

"""Widget for floating-point numbers."""

57

58

class IntegerWidget(NumberWidget):

59

"""Widget for integer numbers."""

60

61

class DecimalWidget(NumberWidget):

62

"""Widget for decimal numbers with precision control."""

63

```

64

65

### Text and Character Widgets

66

67

Widgets for handling text and character data.

68

69

```python { .api }

70

class CharWidget(Widget):

71

def __init__(self, allow_blank=False, **kwargs):

72

"""

73

Widget for character/string data.

74

75

Parameters:

76

- allow_blank: bool, allow blank values (default: False)

77

- **kwargs: Additional widget options

78

"""

79

```

80

81

### Boolean Widget

82

83

Widget for handling boolean data with customizable true/false values.

84

85

```python { .api }

86

class BooleanWidget(Widget):

87

TRUE_VALUES = ['1', 1, True, 'true', 'TRUE', 'True', 'yes', 'YES', 'Yes', 'y', 'Y']

88

FALSE_VALUES = ['0', 0, False, 'false', 'FALSE', 'False', 'no', 'NO', 'No', 'n', 'N']

89

NULL_VALUES = ['', None]

90

91

def __init__(self, **kwargs):

92

"""

93

Widget for boolean data with customizable value mapping.

94

95

Parameters:

96

- **kwargs: Widget configuration options

97

"""

98

```

99

100

### Date and Time Widgets

101

102

Widgets for handling temporal data with format support.

103

104

```python { .api }

105

class DateWidget(Widget):

106

def __init__(self, format='%Y-%m-%d', **kwargs):

107

"""

108

Widget for date data.

109

110

Parameters:

111

- format: str, date format string (default: '%Y-%m-%d')

112

- **kwargs: Additional widget options

113

"""

114

115

class DateTimeWidget(Widget):

116

def __init__(self, format='%Y-%m-%d %H:%M:%S', **kwargs):

117

"""

118

Widget for datetime data.

119

120

Parameters:

121

- format: str, datetime format string (default: '%Y-%m-%d %H:%M:%S')

122

- **kwargs: Additional widget options

123

"""

124

125

class TimeWidget(Widget):

126

def __init__(self, format='%H:%M:%S', **kwargs):

127

"""

128

Widget for time data.

129

130

Parameters:

131

- format: str, time format string (default: '%H:%M:%S')

132

- **kwargs: Additional widget options

133

"""

134

135

class DurationWidget(Widget):

136

"""Widget for duration/timedelta data."""

137

```

138

139

### Data Structure Widgets

140

141

Widgets for complex data structures like JSON and arrays.

142

143

```python { .api }

144

class JSONWidget(Widget):

145

def __init__(self, **kwargs):

146

"""

147

Widget for JSON data serialization/deserialization.

148

149

Parameters:

150

- **kwargs: JSON serialization options

151

"""

152

153

class SimpleArrayWidget(Widget):

154

def __init__(self, separator=',', **kwargs):

155

"""

156

Widget for simple array data.

157

158

Parameters:

159

- separator: str, separator character (default: ',')

160

- **kwargs: Additional widget options

161

"""

162

```

163

164

### Relationship Widgets

165

166

Widgets for handling Django model relationships.

167

168

```python { .api }

169

class ForeignKeyWidget(Widget):

170

def __init__(self, model, field='pk', use_natural_foreign_keys=False, key_is_id=False, **kwargs):

171

"""

172

Widget for foreign key relationships.

173

174

Parameters:

175

- model: Related model class

176

- field: str, field to use for lookup (default: 'pk')

177

- use_natural_foreign_keys: bool, use natural keys for lookup

178

- key_is_id: bool, whether key is the model ID

179

- **kwargs: Additional widget options

180

"""

181

182

def get_queryset(self, value, row, *args, **kwargs):

183

"""

184

Get queryset for foreign key lookup.

185

186

Parameters:

187

- value: Lookup value

188

- row: dict, complete row data

189

- *args: Additional arguments

190

- **kwargs: Additional keyword arguments

191

192

Returns:

193

QuerySet for lookup operations

194

"""

195

196

def get_lookup_kwargs(self, value, row, **kwargs):

197

"""

198

Get lookup kwargs for foreign key resolution.

199

200

Parameters:

201

- value: Lookup value

202

- row: dict, complete row data

203

- **kwargs: Additional options

204

205

Returns:

206

Dict of lookup kwargs

207

"""

208

209

class ManyToManyWidget(Widget):

210

def __init__(self, model, separator=None, field=None, **kwargs):

211

"""

212

Widget for many-to-many relationships.

213

214

Parameters:

215

- model: Related model class

216

- separator: str, separator for multiple values

217

- field: str, field to use for lookup

218

- **kwargs: Additional widget options

219

"""

220

```

221

222

### Utility Functions

223

224

```python { .api }

225

def format_datetime(value, datetime_format):

226

"""

227

Format datetime value using specified format string.

228

229

Parameters:

230

- value: datetime object or string

231

- datetime_format: str, format string

232

233

Returns:

234

Formatted datetime string

235

"""

236

```

237

238

## Usage Examples

239

240

### Basic Widget Usage

241

242

```python

243

from import_export import fields, widgets

244

245

class BookResource(resources.ModelResource):

246

# Use date widget with custom format

247

published_date = fields.Field(

248

attribute='published_date',

249

widget=widgets.DateWidget(format='%d/%m/%Y')

250

)

251

252

# Use boolean widget for active status

253

is_active = fields.Field(

254

attribute='is_active',

255

widget=widgets.BooleanWidget()

256

)

257

258

# Use JSON widget for metadata

259

metadata = fields.Field(

260

attribute='metadata',

261

widget=widgets.JSONWidget()

262

)

263

```

264

265

### Custom Widget Creation

266

267

```python

268

class UpperCaseWidget(widgets.CharWidget):

269

"""Custom widget that converts text to uppercase."""

270

271

def clean(self, value, row=None, **kwargs):

272

value = super().clean(value, row, **kwargs)

273

return value.upper() if value else value

274

275

def render(self, value, obj=None, **kwargs):

276

return value.upper() if value else value

277

278

class BookResource(resources.ModelResource):

279

title = fields.Field(

280

attribute='title',

281

widget=UpperCaseWidget()

282

)

283

```

284

285

### Foreign Key Widget with Custom Lookup

286

287

```python

288

class AuthorWidget(widgets.ForeignKeyWidget):

289

"""Custom foreign key widget for author lookup."""

290

291

def __init__(self, **kwargs):

292

super().__init__(Author, field='name', **kwargs)

293

294

def get_queryset(self, value, row, *args, **kwargs):

295

# Custom queryset filtering

296

return Author.objects.filter(active=True)

297

298

def get_lookup_kwargs(self, value, row, **kwargs):

299

# Custom lookup logic

300

return {'name__iexact': value.strip()}

301

302

class BookResource(resources.ModelResource):

303

author = fields.Field(

304

attribute='author',

305

widget=AuthorWidget()

306

)

307

```

308

309

### Many-to-Many Widget with Custom Separator

310

311

```python

312

class TagWidget(widgets.ManyToManyWidget):

313

"""Custom M2M widget for tags."""

314

315

def __init__(self, **kwargs):

316

super().__init__(Tag, separator='|', field='name', **kwargs)

317

318

def clean(self, value, row=None, **kwargs):

319

if not value:

320

return []

321

322

# Split by separator and clean each tag

323

tag_names = [name.strip() for name in value.split(self.separator)]

324

tags = []

325

326

for name in tag_names:

327

if name:

328

tag, created = Tag.objects.get_or_create(name=name)

329

tags.append(tag)

330

331

return tags

332

333

class BookResource(resources.ModelResource):

334

tags = fields.Field(

335

attribute='tags',

336

widget=TagWidget()

337

)

338

```

339

340

### Date Widget with Multiple Formats

341

342

```python

343

class FlexibleDateWidget(widgets.DateWidget):

344

"""Date widget that accepts multiple input formats."""

345

346

FORMATS = ['%Y-%m-%d', '%d/%m/%Y', '%m/%d/%Y', '%Y%m%d']

347

348

def clean(self, value, row=None, **kwargs):

349

if not value:

350

return None

351

352

if isinstance(value, date):

353

return value

354

355

# Try multiple formats

356

for fmt in self.FORMATS:

357

try:

358

return datetime.strptime(str(value), fmt).date()

359

except ValueError:

360

continue

361

362

raise ValueError(f"Unable to parse date: {value}")

363

364

class BookResource(resources.ModelResource):

365

published_date = fields.Field(

366

attribute='published_date',

367

widget=FlexibleDateWidget()

368

)

369

```

370

371

### Widget with Row Context

372

373

```python

374

class ConditionalWidget(widgets.CharWidget):

375

"""Widget that behaves differently based on row data."""

376

377

def clean(self, value, row=None, **kwargs):

378

value = super().clean(value, row, **kwargs)

379

380

# Modify value based on other row data

381

if row and row.get('category') == 'special':

382

return f"SPECIAL: {value}"

383

384

return value

385

386

class BookResource(resources.ModelResource):

387

title = fields.Field(

388

attribute='title',

389

widget=ConditionalWidget()

390

)

391

```

392

393

### Complex Data Transformation Widget

394

395

```python

396

class PriceWidget(widgets.Widget):

397

"""Widget for handling price data with currency conversion."""

398

399

def __init__(self, currency='USD', **kwargs):

400

self.currency = currency

401

super().__init__(**kwargs)

402

403

def clean(self, value, row=None, **kwargs):

404

if not value:

405

return None

406

407

# Handle different price formats

408

if isinstance(value, str):

409

# Remove currency symbols and convert

410

value = value.replace('$', '').replace(',', '').strip()

411

412

try:

413

price = Decimal(str(value))

414

415

# Convert currency if needed

416

if row and row.get('currency') != self.currency:

417

price = self.convert_currency(

418

price, row.get('currency'), self.currency

419

)

420

421

return price

422

except (ValueError, TypeError):

423

raise ValueError(f"Invalid price format: {value}")

424

425

def render(self, value, obj=None, **kwargs):

426

if value is None:

427

return ''

428

return f"${value:.2f}"

429

430

def convert_currency(self, amount, from_currency, to_currency):

431

# Mock currency conversion

432

rates = {'USD': 1.0, 'EUR': 0.85, 'GBP': 0.75}

433

return amount * rates.get(to_currency, 1.0) / rates.get(from_currency, 1.0)

434

435

class BookResource(resources.ModelResource):

436

price = fields.Field(

437

attribute='price',

438

widget=PriceWidget(currency='USD')

439

)

440

```

441

442

### Widget with Validation

443

444

```python

445

class ISBNWidget(widgets.CharWidget):

446

"""Widget for ISBN validation and formatting."""

447

448

def clean(self, value, row=None, **kwargs):

449

value = super().clean(value, row, **kwargs)

450

451

if not value:

452

return value

453

454

# Clean ISBN format

455

isbn = ''.join(c for c in value if c.isdigit() or c.upper() == 'X')

456

457

# Validate ISBN length

458

if len(isbn) not in [10, 13]:

459

raise ValueError(f"Invalid ISBN length: {isbn}")

460

461

# Add hyphens for readability

462

if len(isbn) == 13:

463

return f"{isbn[:3]}-{isbn[3:4]}-{isbn[4:6]}-{isbn[6:12]}-{isbn[12]}"

464

else:

465

return f"{isbn[:1]}-{isbn[1:4]}-{isbn[4:9]}-{isbn[9]}"

466

467

def render(self, value, obj=None, **kwargs):

468

# Remove hyphens for export

469

return ''.join(c for c in (value or '') if c.isdigit() or c.upper() == 'X')

470

471

class BookResource(resources.ModelResource):

472

isbn = fields.Field(

473

attribute='isbn',

474

widget=ISBNWidget()

475

)

476

```

477

478

### Widget Configuration in Resource Meta

479

480

```python

481

class BookResource(resources.ModelResource):

482

class Meta:

483

model = Book

484

widgets = {

485

'published_date': {'format': '%d/%m/%Y'},

486

'price': {'currency': 'EUR'},

487

'is_active': {'true_values': ['yes', 'y', '1']},

488

'tags': {'separator': ';'},

489

}

490

```