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
```